类的重用

类继承的概念和语法:

  1. java之指出类的单继承,即没一个子类只有一个直接的超类。
//继承的语法
[ClassModifier] class ClassName extends SuperClassName
{ 
//类体
}
//e.g.(Manager->Employee->Person)
public class Person {
  public String name;
  public String getName() { 
   return name; 
  }
} 
public class Employee extends Person { 
    public int employeeNumber; 
    public int getEmployeeNumber() { 
       return employeeNumber; 
    } 
} 
public class Manager extends Employee { 
    public String responsibilities; 
    public String getResponsibilities() { 
      return responsibilities;
    } 
}

public class Exam3_2Test {
  public static void main(String args[]){
    Employee li = new Employee(); 
    li.name = "Li Ming"; 
    li.employeeNumber = 123456; 
    System.out.println(li.getName());
    System.out.println(li.getEmployeeNumber()); 
    
    Manager he = new Manager(); 
    he.name = "He Xia"; 
    he.employeeNumber = 543469;           
    he.responsibilities = "Internet project"; 
    System.out.println(he.getName()); 
    System.out.println(he.getEmployeeNumber());
    System.out.println(he.getResponsibilities());
  }
}

子类不能直接访问从超类中继承的私有属性及方法,但可使用公有(及保护)方法进行访问

public class B { 
   public int a = 10; 
   private int b = 20; 
   protected int c = 30; 
   public int getB()  { return b; } 
} 
public class A extends B { 
   public int d; 
   public void tryVariables() { 
      System.out.println(a);             //允许 
      System.out.println(b);             //不允许
      System.out.println(getB());     //允许 
      System.out.println(c);             //允许 
    } 
}

隐藏和覆盖:

隐藏:

子类中声明了与超类中相同的成员变量名

  • 从超类继承的变量将被隐藏
  • 子类拥有了两个相同名字的变量,一个继承自超类,另一个由自己声明
  • 当子类执行继承自超类的操作时,处理的是继承自超类的变量,而当子类执行它自己声明的方法时,所操作的就是它自己声明的变量
  • 调用从超类继承的方法,则操作的是从超类继承的属性
  • 本类中声明的方法使用“super.属性”访问从超类继承的属性
class A1
{   int x = 2;    
    public void setx(int i) 
    {   x = i;    }
    void printa()
    {System.out.println(x);}  
}
class B1 extends A1
{   int x=100;
    void printb() 
    {  super.x = super.x +10 ;
       System.out.println("super.x=" +
                    super.x + "  x= " + x);
    }  
}

public class Exam3_5_1Tester 
{  public static void main(String[] args)
    {   A1 a1 = new A1();  
         a1.setx(4);   
         a1.printa();

         B1 b1 = new B1();     
         b1.printb();     
         b1.printa();  
  
         b1.setx(6);  // 将继承x值设置为6
         b1.printb();     
         b1.printa();
         a1.printa();
    }  

//运行结果:
4
super.x= 12  x= 100
12
super.x= 16  x= 100
16
4

覆盖:

  • 如果子类不需使用从超类继承来的方法的功能,则可以声明自己的同名方法,称为方法覆盖
  • 覆盖方法的返回类型,方法名称,参数的个数及类型必须和被覆盖的方法一摸一样
  • 只需在方法名前面使用不同的类名或不同类的对象名即可区分覆盖方法和被覆盖方法
  • 覆盖方法的访问权限可以比被覆盖的宽松,但是不能更为严格

覆盖的应用场合

  • 子类中实现与超类相同的功能,但采用不同的算法或公式 ;
  • 在名字相同的方法中,要做比超类更多的事情 ;
  • 在子类中需要取消从超类继承的方法。

注意事项:

  • 必须覆盖的方法

    • 派生类必须覆盖基类中的抽象的方法,否则派生类自身也成为抽象类.
  • 不能覆盖的方法

    • 基类中声明为final的终结方法
    • 基类中声明为static 的静态方法
  • 调用被覆盖的方法super.overriddenMethodName();

子类的构造方法:

原则:

  • 子类不能从超类继承构造方法
  • 最好在子类的构造方法中使用super关键字显式调用超类的某个构造方法,调用语句必须出现在子类构造方法的第一行。
  • 如子类构造方法体中没有显式调用超类构造方法,则系统在执行子类的构造方法时会自动调用超类的默认构造方法(即无参的构造方法)
public class Person2 {
    protected String name, phoneNumber, address;
    public Person2() {
        this("", "", "");
    }
    public Person2(String aName, String aPhoneNumber, String anAddress) {
        name = aName;
        phoneNumber = aPhoneNumber;
        address = anAddress;
    }
}

public class Employee2 extends Person2 {
    protected int employeeNumber;
    protected String workPhoneNumber;
    public Employee2() {
        //此处隐含调用构造方法 Person2()
        this(0, "");
    }
    public Employee2(int aNumber, String aPhoneNumber) {
        //此处隐含调用构造方法 Person2()
        employeeNumber = aNumber;
        workPhoneNumber = aPhoneNumber;
    }
}

public class Professor extends Employee2 {
    protected String research;
    public Professor() {
        super();
        research = "";
    }
    public Professor(int aNumber, String aPhoneNumber, String aResearch) {
        super(aNumber, aPhoneNumber);
        research = aResearch;
    }
}

object类*:

  • public final Class getClass() 获取当前对象所属的类信息,返回Class对象。
  • public String toString() 返回表示当前对象本身有关信息的字符串对象。
  • public boolean equals(Object obj) 比较两个对象引用是否指向同一对象,是则返回true,否则返回false。
    • 在Object类中声明的equals()方法功能是比较两个对象引用是否指向同一对象,而不是比较两个引用指向的对象是否相等。
    • 两个对象具有相同的类型,及相同的属性值,则称二者相等(equal)。
    • 如果两个引用变量指向的是同一个对象,则称这两个引用变量同一(identical)。
    • 两个对象同一,则肯定相等。
    • 两个对象相等,不一定同一。比较运算符“==” 判断的是这两个对象是否同一。
  • protected Object clone( ) 复制当前对象,并返回这个副本。
  • Public int hashCode() 返回该对象的哈希代码值。
  • protected void finalize() throws Throwable 在对象被回收时执行,通常完成的资源释放工作。

终结类与终结方法:

  • 用final修饰的类和方法;
  • 终结类不能被继承;
  • 终结方法不能被子类覆盖。

终结类:

final class ChessAlgorithm {   
    . . .
}

class BetterChessAlgorithm extends ChessAlgorithm {
    … 
}

//报错:Chess.java:6: Can't subclass final classes: class ChessAlgorithm
class BetterChessAlgorithm extends ChessAlgorithm {
      ^
1 error

终结方法:

class Parent
{
   public Parent() {   } //构造方法
   final int getPI() { return Math.PI; } //终结方法
}
//说明
//getPI()是用final修饰符声明的终结方法,不能在子类中对该方法进行覆盖,因而如下声明是错的:
Class Child extends Parent
{
   public Child() {}
   int getPI() { return 3.14; } //错!不允许覆盖超类中的终结方法
}

抽象类:

  • 代表一个抽象概念的类;
  • 规定整个类家族都必须具备的属性和行为。
  • 类名前加修饰符abstract;
  • 可包含常规类能包含的任何成员,包括非抽象方法;
  • 也可包含抽象方法:用abstract修饰,只有方法原型,没有方法的实现;
  • 没有具体实例对象的类,不能使用new方法进行实例化,只能用作超类;
  • 只有当子类实现了抽象超类中的所有抽象方法,子类才不是抽象类,才能产生实例;
  • 如果子类中仍有抽象方法未实现,则子类也只能是抽象类。
//抽象类的声明
abstract class Number {
    . . .
}
new Number();
//编译器将显示错误

抽象方法:

  • 规定子类应该具有的行为,但在抽象超类中尚不能实现的方法,可以声明为抽象方法。

  • 声明的语法形式为:

    - public abstract <returnType> <methodName>(...);
    - 
    

    仅有方法原型,而没有方法体;

  • 抽象方法的具体实现由子类在它们各自的类声明中完成;只有抽象类可以包含抽象方法。

泛型

  • 泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数;
  • 可以声明泛型类、泛型方法和泛型接口(下一章介绍接口)。
class GeneralType1 {
    Object object;
    public GeneralType1(Object object) {
        this.object = object;
    }
    public Object getObj() {
        return object;
    }
}

public class GenericsTester1 {
    public static void main(String[] args) {
        // 传递参数为int类型的2,会自动封箱为Integer类型的对象。
        GeneralType1 i = new GeneralType1(2);
        // 传递参数为double类型的0.33,会自动封箱为Double类型的对象。
        GeneralType1 d = new GeneralType1(0.33);
        System.out.println("i.object=" + (Integer) i.getObj());
        // 可以通过编译,但运行时异常
        System.out.println("i.object=" + (Integer) d.getObj());
    }
}

类的组合:

  • 面向对象的程序用软件对象来模仿现实世界的对象:现实世界中,大多数对象由更小的对象组成;与现实世界的对象一样,软件中的对象也常常是由更小的对象组成。
  • Java的类中可以有其他类的对象作为成员,这便是类的组合。
  • 组合也是一种重用机制,可以使用“有一个” 来描述这种关系。
//将已存在类的对象放到新类中即可
//例如,可以说“厨房( kitchen)里有一个炉子(cooker)和一个冰箱(refrigerator)”。所以,可简单的把对象myCooker和myRefrigerator放在类Kitchen中:
class Cooker{   // 类的语句  }
class Refrigerator{   // 类的语句}
class Kitchen{   
    Cooker myCooker;
    Refrigerator myRefrigerator;
}
//e.g.
public class Point   //点类 
{
   private int x, y;  //coordinate
   public Point(int x, int y) { this.x = x; this.y = y;}
   public int GetX()  {  return x; }
   public int GetY()  {  return y; }
}

class Line   //线段类
{
   private Point  p1,p2;     // 两端点
   Line(Point a, Point b) {  
      p1 = new Point(a.GetX(),a.GetY());
      p2 = new Point(b.GetX(),b.GetY());
   }
    public double Length() {  
      return Math.sqrt(Math.pow(p2.GetX()-p1.GetX(),2) 
                    + Math.pow(p2.GetY()-p1.GetY(),2));
    }
}

接口和多态

接口:

  • 可以看做是一个“纯”抽象类,它只提供一种形式,并不提供实现
  • 接口中可以规定方法的原型:方法名、参数列表以及返回类型,但不规定方法主体;
  • 也可以包含基本数据类型的数据成员,但它们都默认为static和final。
  • 是面向对象的一个重要机制
  • 继承多个设计。
  • 建立类和类之间的“协议”
    • 将类根据其实现的功能分组用接口代表,而不必顾虑它所在的类继承层次;这样可以最大限度地利用动态绑定,隐藏实现细节;
    • 实现不同类之间的常量共享。

接口的语法:

[接口修饰符] interface 接口名称 [extends 父接口名]{
      …//方法的原型声明或静态常量
}
  • 接口的数据成员一定要有初值,且此值将不能再更改,可以省略final关键字。
  • 接口中的方法必须是“抽象方法”,不能有方法体,可以省略public及abstract关键字。
//e.g.
声明一个接口Shape2D,包括π和计算面积的方法原型
interface Shape2D{         //声明Shape2D接口
    final double pi=3.14;     //数据成员一定要初始化
    public abstract double area(); //抽象方法
}
在接口的声明中,允许省略一些关键字,也可声明如下
interface Shape2D{           
   double pi=3.14;            
   double area();             
}

实现接口:

public class 类名称 implements 接口名称 { 
        //在类体中实现接口的方法 
        //本类声明的更多变量和方法
}

注意:

  1. 必须实现接口中的所有方法;
  2. 来自接口的方法必须声明成public。

Lambda表达式:

  • 从Java 8开始,可使用Lambda表达式实现函数式接口编程
  • Lambda表达式使程序更加简洁、清晰、易于编写
  • Lambda表达式由三部分组成:
    • 参数表。由于参数类型已经由接口声明,因此Lambda表达式的参数表仅包含参数值,如果接口方法包含了两个参数,则参数表形如(p1, p2)。
    • 箭头操作符“->”。
    • 表达式体。类似于普通的Java方法,可以对参数进行操作,也可以有返回值。

实现多个接口的语法:

  • Java不允许一个类有多个超类一

  • 个类可以实现多个接口,通过这种机制可实现对设计的多重继承。

  • 实现多个接口的语法如下

    [类修饰符] class  类名称  implements 接口1, 接口2,  …{… …}
    

接口的扩展:

  • 接口可通过扩展的技术派生出新的接口
    • 原来的接口称为超接口(super interface);
    • 派生出的接口称为子接口(sub interface)。
  • 实现一个接口的类也必须实现其超接口。
  • 接口扩展的语法
interface 子接口的名称 extends 超接口的名称1,超接口的名称2,…{  … …}

例:实现如图所示接口

​ Shape是超接口,Shape2D与Shape3D是其子接口。Circle类及Rectangle类实现接口Shape2D,而Box类及Sphere类实现接口Shape3D

// 声明Shape接口
interface Shape{             
  double pi=3.14;            
  void setColor(String str); 
}
//声明Shape2D接口扩展了Shape接口
interface Shape2D extends Shape {
    double area();
}

class Circle implements Shape2D {
    double radius;
    String color;
    public Circle(double r) {   radius=r;  }
    @Override
    public double area() { return (pi*radius*radius); }
    @Override
    public void setColor(String str){
       color=str;
       System.out.println("color="+color);
    }
}
public class InterfaceTester3 {   //测试类
  public static void main(String []args) {
     Circle cir;
     cir=new Circle(2.0);
     cir.setColor("blue");
     System.out.println("Area = " + cir.area());
  }
}

/*说明
首先声明了父接口Shape,然后声明其子接口Shape2D;
之后声明类Circle实现Shape2D子接口,因而在此类内必须明确定义setColor()与area()方法的处理方式;
最后在主类中我们声明了Circle类型的变量cir并创建新的对象,最后通过cir对象调用setColor()与area()方法。*/

类型转换:

  • 转换方式:
    • 隐式的类型转换;
    • 显式的类型转换。
  • 转换方向:
    • 向上转型;
    • 向下转型。

转换规则:

  • 基本类型之间的转换将值从一种类型转换成另一种类型。
  • 引用变量的类型转换
    • 将引用转换为另一类型的引用,并不改变对象本身的类型。
    • 只能被转为
      • 任何一个(直接或间接)超类的类型(向上转型);
      • 对象所属的类(或其超类)实现的一个接口(向上转型);
      • 被转为引用指向的对象的类型(唯一可以向下转型的情况)。
  • 当一个引用被转为其超类引用后,通过他能够访问的只有在超类中声明过的方法。

例:

​ Manager对象可以被塑型为Employee、Person、Object或Insurable,不能被塑型为Customer、Company或Car .

隐式类型转换:

  • 基本数据类型
    • 可以转换的类型之间,存储容量低的自动向存储容量高的类型转换。
  • 引用变量被转成更一般的类,例如:
Employee  emp; 
emp = new Manager(); /*将Manager类型的对象直接赋给                      Employee类的引用变量,系统会                   自动将Manage对象塑型为Employee类
  • 被塑型为对象所属类实现的接口类型,例如:

    Car  jetta = new Car(); 
    Insurable  item = jetta; 
    

显示类型转换

  • 基本数据类型

    • (int)871.34354; // 结果为 871
    • (char)65; // 结果为‘A’
    • (long)453; // 结果为453L
  • 引用变量

    • Employee emp;

    • Manager man;

    • emp = new Manager();

    • man = (Manager)emp; //将emp显式转换为它指向的对象的类型