Java—类的封装、继承与多态

时间:2023-02-03 21:56:20

一.类和对象

1.类

类是数据以及对数据的一组操作的封装体。

类声明的格式:

类声明

{

  成员变量的声明;

  成员方法的声明及实现;

}

1.1 声明类

[修饰符] class 类<泛型> [extends 父类] [implements 接口列表]

<泛型>——类的参数,带有参数的类成为泛型类。

1.2 声明成员变量和成员方法

成员变量声明格式:[修饰符] 数据类型 变量[=表达式]{,变量[=表达式]};

成员方法——用来描述对成员变量进行的操作,格式:[修饰符] 返回值类型 方法([参数列表])[throws 异常类]
{
   语句序列;
   [return [返回值]];
}

例如:

public class MyDate   //类声明

{

  int year,month,day;  //声明成员变量

  void set(int y,int m,int d)   //成员方法,设置日期值

  {                                    //无返回值,有三个参数

      year=y;

      month=m;

      day=d;

  } 

}

2.对象

类本身不参与程序运行,实际参与运行的是类的对象。

对象是类的实例(instance),即类的取值。类的一个对象能够获得并保存类的一个实例。

类比:类——对象——类的实例

    数据类型———变量——取值

    例如:int类型——变量i——取值10

2.1 MyDate d=new MyDate();//声明对象,创建实例并赋值

2.2 引用对象的成员变量和调用成员方法

    对象.成员变量

    对象.成员方法([参数列表])

注:Java的内存自动管理机制,能够跟踪存储单元的使用情况,自动回收不再被使用的资源,所以,程序中不需要释放对象占用的空间资源。

Java的类是引用数据类型,两个对象之间的赋值是引用赋值,对象赋值过程中没有创建新的实例。

(注:1、“==”作用在引用数据类型间,判断内存地址是否相等,想判断内容实体用equals;
    2、基本传值传参传的是副本,修改后原值不变;引用传值传参穿的是地址,修改后原值改变。

  引用类型 就是只变量中存储的不是值而是一个内存中的地址的数据类型
  也就是说 变量中存储了这个变量的值所在内存中的地址 每次调用这个变量都是引用这个地址而得到真正的值 所以叫引用类型 。
  通俗说,将变量名与变量值分开,比如Student s=new Student(),s就是个引用类型的变量,s这个名分配在栈空间里,但是s它指向的值在堆空间里

二、类的封装性

1.构造方法

用于创建类的一个实例并对实例的成员变量进行初始化。构造方法与类同名;构造方法通过new运算符调用。

构造方法和成员方法的不同之处:

A.作用不同:成员方法实现对类中成员变量的操作;构造方法用于创建类的实例并对实例的成员变量进行初始化。

B.调用方式不同:成员方法通过对象调用;构造方法通过new运算符调用。

例如:

public class MyDate

{

  public MyDate (int y,int m,int d)

  {

    year=y;month=m;day=d;

  }

}

使用new运算符调用自定义的构造方法,参数必须符合构造方法的声明。

MyDate d=new MyDate(2009,7,18);//创建实例并初始化成员变量

拷贝构造方法

一个类的构造方法,如果其参数是该类对象,称为拷贝构造方法,它将创建的新对象初始化为形式参数的实例值,实现对象复制功能。

Java不提供默认拷贝构造方法。

public class MyJava {

 public static void main(String[] args) {
  // TODO 自动生成的方法存根
  MyJava d1=new MyJava(2016,4,27);
  MyJava d2=new MyJava(d1);
  System.out.print(d2.year);
 }

 private int year;
 private int month;
 private int day;
 
public MyJava(int y,int m,int d){
 year = y;
    month=m;
 day=d;
}
public MyJava(MyJava d){
 year = d.year;
    month=d.month;
 day=d.day;
}

输出:2016

2.this引用和instanceof 对象运算符

2.1 this引用

(1)指代对象本身 this

this用于指代调用成员方法的当前对象自身。

(2)访问本类的成员变量和成员方法

this.成员变量

this.成员方法([参数列表])

public MyDate(int year,int month,int day)

{  //指定参数的构造方法,参数与成员变量同名

  this.year=year;//this.year指定当前对象的成员变量,year指参数

  this.month=month;//this引用不能省略

  this.day=day;  

}

public MyDate()   //默认构造方法,重载

{

  this(1970,1,1);//调用本类已定义好的构造方法

}

public MyDate(MyDate d)

{

  this(d.year,d.month,d.day);  

}

2.2 instanceof 对象运算符

判断一个对象是否属于指定类及其子类,返回boolean类型。

例:MyDate d=new MyDate();

    d instanceof MyDate

                     //返回true,d是MyDate类的实例

3 访问控制

3.1 类的访问控制权限

声明一个类可使用的权限修饰符只有两种: public 和 缺省

  共有权限public,可被所有类访问

  缺省权限无修饰符,可被当前包(当前文件夹)中的类访问。

一个源程序文件中可声明多个类,但用public修饰的类只能有一个,且该雷鸣必须与文件名相同。

  源程序文件MyDate.java中可声明多个类如下:

    public class MyDate  //公有权限的类

    class MyDate_ex //缺省权限的类

类的成员有4种访问控制权限

private—声明私有成员,该成员仅能被当前类的成员访问。(当前类)

缺省—没有修饰符表示缺省权限,说明该成员能被当前类以及当前包中的其他类访问,也称在当前包中可见。(当前包)

protected—声明保护成员,该成员能被当前类及其子类、或当前包中的其他类访问,也称在子类中可见。(子类)

public——声明公有成员,该成员可被所有类访问。(所有)

声明set()和get()方法存取对象的属性 —值对象的某个特性或特征

  Java约定,存取对象属性的方法分别是set()和get()。

    public void set(int y,int m,int day)//设日期

    public void set(MyDate d)//设置日期,重载

    public int getYear()//获取年份

4 静态成员

实例成员—属于对象,包括实例成员变量和成员方法。只有创建了实例,才能通过对象访问实例成员变量和实例成员方法。

静态成员—属于类,需要用关键字static标识。包括类成员变量和类成员方法。即使没有创建实例,也可以通过类名直接访问类静态成员变量和调用静态成员方法。

5 析构方法

——用于释放实例并执行特定操作。

public void finalize() //约定析构方法名为finalize

{                           //finalize()没有参数,也没有返回值

  语句序列;

}                          //一个类只能有一个finalize()方法,qiefinalize()不允重载

  Java的内存自动管理机制能够释放不再将被使用的对象,所以,通常类不需要设计析构方法。

   如果需要在释放对象时执行特定操作,则类可以声明析构方法。

注:A.当对象超出它的作用域时,Java将执行对象的析构方法。

  B.一个对象也可以调用析构方法来释放对象自己。

  C.不能使用已被析构方法释放的对象,否则将产生运行错误。

     例如:d.finalize(); //调用对象的析构方法

6 浅拷贝与深拷贝

浅拷贝—一个类的拷贝构造方法,使用一个已知实例对新创建实例的成员变量逐个赋值,这种方法称为浅拷贝。

  当对象的成员变量是基本数据类型时,两个对象的成员变量已有存储空间,赋值运算传递值,所以浅拷贝能够赋值实例。

  当对象的成员变量是引用数据类型时,浅拷贝不能实现对象复制功能,需要深拷贝。

  public class Person

  {

    String name;                     //姓名

     MyDate birthday;                //生日

     public Person(String name,MyDate birthday)   //构造方法

  {

    this.name=name;

    this.birthday=birthday;    //引用赋值

  }

    public Person(Person p)          //拷贝构造方法,复制对象

  {

    this(p.name,p.birthday);

  }  

 }

深拷贝—当一个类包含引用类型的成员变量时,该类的拷贝构造方法,不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建新的实例,并初始化为形式参数实例值,这种复制方法称为深拷贝。

三 类的继承

1 由继承派生类

继承:根据一个已知的类由继承方式创建一个类,使新创建的类自动拥有被继承类的全部成员。

父类/超类——被继承类;子类/派生类——通过继承产生的新类

父类和子类的关系:

A.子类自动拥有父类的全部成员,包括成员变量和方法等,使父类成员得以传承和延续;

B.子类可以更改从父类继承来的成员,使父类成员适应新的需求;

C.子类也可以增加自己的成员,使类的功能得以扩充。

D.子类不能删除父类的成员。

声明继承类:[修饰符] class <泛型>[extends 父类] [implements 接口列表]

注:Java的类是单继承的,一个类只能有一个父类,不能有多个父类。

1.1 继承原则

(1)子类继承父类的成员变量

(2)子类继承父类除构造方法以外的成员方法

(3)子类不能继承父类的构造方法

    子类没有继承父类的构造方法,因为父类的构造方法只能创建父类实例并初始化,无法创建实例。

    例如:父类声明构造方法Person(String,MyDate),当子类没有生命Student(String,MyDate)构造方法时,下列语句是错误的:

      Student s1=new Student("李小明",new MyDate(1979,3,15));//语法错,构造方法参数不匹配

(4)子类可以增加成员,可以重定义从父类继承来的成员,但不能删除。

1.2 子类的构造方法

子类对象包含从其父类继承来的成员变量,以及子类声明的成员变量,子类构造方法必须对这些成员变量进行初始化。而父类构造方法已对父类声明的成员变量进行了初始化,因此,子类构造方法必须调用父类的某一个构造方法。

1.2.1 使用super()调用父类构造方法

     在子类的构造方法体中,可以使用"super引用"调用父类的构造方法。其语法如下:

              super([参数列表])

     public Student(String name,MyDate birthday,String spec)

     {

       super(name,birthday);   //super()调用必须是第一条语句  调用父类同参数的构造方法

       this.speciality=spec;

     }

1.2.2 默认执行super()

以下两种情况,Java默认值型super(),调用父类的构造方法。

(1)当一个类没有声明构造方法时,Java为该类提供默认构造方法,调用super()执行父类无参数的构造方法。

  public Student()    //Java提供的默认构造方法

  {

      super();     //调用父类构造方法Person()

  }

  如果父类Person没有声明构造方法Person(),则上述语句产生编译错误。因此,每个类都要声明无参数的构造方法,即使自己不用,也要为子类准备着。

      public Person()   //Java提供的默认构造方法

      {

        super();        //调用父类构造方法Object()

      }

(2) 如果子类的构造方法没有调用super()或this(),Java将默认执行super()。

      public Student()

      {

        super();

        speciality="";

      }

1.3 类的多态性

类的多态性表现为方法的多态和类型的多态。

方法的多态:包括方法的覆盖和重载,为一种功能提供多种实现。

类型的多态:子类是一种父类类型。

子类重定义父类成员包括:

(1)重定义父类的成员变量,则隐藏父类的成员变量;

(2)重定义父类的成员方法,如果参数列表相同,则覆盖父类的成员方法,否则重载。

子类重定义父类成员表现出多态,父类对象引用父类成员,子类对象引用子类成员。

在子类的成员方法中,如果需要引用被子类隐藏或覆盖的父类同名成员,可以用super引用。格式如下:

super.成员变量

super.成员方法([参数列表])

子类对象即是父类对象——子类对象包含父类的所有成员变量,isA关系表现为继承具有“即是”性质。 new Person()/Student() instanceof Person

父类对象引用子类实例——父类对象能够引用子类实例

Person p=new Student();

1.3.1 编译时多态和运行时多态

 (1)编译时多态

    对于多个同名方法,如果在编译时能够确定执行同名方法中的哪一个,则称编译时多态。

      方法的重载—都是编译时多态性

    方法的覆盖—当对象引用本类实例时,为编译时多态;

                    否则,运行时多态。

   Person p1=new Person("李小明",new MyDate(1979,3,15));

   System.out.println("p1:"+p1.toString()); //编译时多态性,执行Person类的toString()

  (2)运行时多态

    父类对象只能执行哪些在父类中声明、被子类覆盖了的子类方法,如toString(),不能执行子类中新增加的成员方法。

程序运行时,Java从实例所属的类开始寻找匹配的方法执行,如果当前类中没有匹配方法,则沿着继承关系逐层向上,依次在父类和各祖先类中寻找匹配方法,直到Object类。

四 类的抽象性

抽象类特点:1)抽象类中可以不包含抽象方法,但是包含抽象方法的类必须被声明为抽象类;

               2)构造方法、静态成员方法不能被声明为抽象方法;

        3)一个非抽象类必须实现从父类继承来的所有抽象方法,如果不能实现父类的所有抽象方法,则该类必须声明为抽象类;

        4)抽象类不能被实例化,不能创建抽象类实例。