Loading

Java基础-day18

抽象类

抽象类不可以用于创建对象 。 抽象类可以包含抽象方法 , 这些方法将在具体的子类中实现

抽象类:一个非常抽象的父类,它都没有任何具体的实例

抽象方法:利用abstract修饰符来定义的方法,之后,这个方法所属的类为抽象类

public abstract class GeometricObject {     // 抽象类
    private String color = "white";
    private boolean isFilled;
    private java.util.Date dataCreated;

    protected GeometricObject(){
        dataCreated = new java.util.Date();
    }

    protected GeometricObject(String color, boolean isFilled){
        this.color = color;
        this.isFilled = isFilled;
        dataCreated = new java.util.Date();
    }

    public String getColor(){
        return color;
    }

    public void  setColor(String color){
        this.color = color;
    }

    public boolean getisFilled(){
        return isFilled;
    }

    public void setisFilled(boolean isFilled){
        this.isFilled = isFilled;
    }

    public java.util.Date getDataCreated(){
        return dataCreated;
    }

    @Override
    public String toString(){
        return "create on " + dataCreated + "\ncolor:" + color +
                " and filled: " + isFilled;
    }

    // 抽象方法
    public abstract double getArea();
    public abstract double getPerimeter();

}

Snipaste_2020-05-02_15-10-26

  1. 抽象类和常规类很像 , 但是不能使用 new 操作符创建它的实例 。 抽象方法只有定义而没有实现 。 它的实现由子类提供 。 一个包含抽象方法的类必须声明为抽象类

  2. 抽象类的构造方法定义为 Protected , 因为它只被子类使用 。 创建一个具体子类的实例时 , 它的父类的构造方法被调用以初始化父类中定义的数据域

为何要使用抽象方法

Snipaste_2020-05-02_15-31-42

JVM 在运行时根据对象的类型动态地决定调用哪一个方法

抽象类的几点说明

Snipaste_2020-05-02_15-36-51

实例学习:抽象的Number类

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;

public class LargestNumber {
    public static void main(String[] args) {
        ArrayList<Number> list = new ArrayList<>();	// 创建一个Number对象的ArrayList
        list.add(4); // 向列表中增加一个 Integer 对象,通过拆箱操作,45自动转换为 Integer对象并增加到列表中
        list.add(4.5);		//  一个 Double 对象
        list.add(new BigInteger("199910017572"));	// 一个BigInteger对象
        list.add(new BigDecimal("3.505211992"));	// 一个BigDecimal对象

        System.out.println("The Largest is : " + getLargest(list));
    }
	// 返回列表中的最大数值
    public static Number getLargest(ArrayList<Number> list){
        if(list.size() == 0 || list == null)
            return  null;
        Number number = list.get(0);        // 获得数组列表中的第一个元素
        for (int i = 1; i < list.size(); i++){
            // doubleValue()方法定义在Number类中,并在Number类的具体子类中得到实现 
            if(number.doubleValue() < list.get(i).doubleValue()){
                number = list.get(i);
            }
        }
        return number;
    }
}

接口

接口是一种与类相似的结构 , 只包含常量和抽象方法

**接口在许多方面都与抽象类很相似 , 但是它的目的是指明相关或者不相关类的多个对象的共同行为 **

修饰符 interface 接口名{
    /** 常量声明 */
    /** 方法签名 */
}

在 Java 中, 接口被看作是一种特殊的类 。就像常规类一样,**每个接口都被编译为独立的字节码文件 **

可以使用接口作为引用变量的数据类型或类型转换的结果等 。 与抽象类相似 , 不能使用 new 操作符创建接口的实例

类和接口之间的关系称为接口继承。 因为接口继承类继承本质上是相同的 , 所以我们将它们都简称为继承

public class TestEdible {
    public static void main(String[] args) {
        Object[] objects={new Chicken(),new Tiger(),new Orange()};//创建对象数组
        for (int i=0;i<objects.length;i++){
            if (objects[i]instanceof Edible)
                System.out.println(((Edible)objects[i]).howToEat());
            if (objects[i]instanceof Animal)
                System.out.println(((Animal)objects[i]).sound());
        }
    }
}

interface Edible {					//设计一个Edible接口来明确一个对象是否是可食用的
    public abstract String howToEat();	//接口中方法格式固定:public abstract
}

abstract class Animal {				//抽象类Animal
    public abstract String sound();	//抽象方法(动物叫声)
}

class Chicken extends Animal implements Edible {//鸡肉类继承抽象的Animal类并实现Edible接口以表明小鸡是可食用的
    @Override
    public String sound() {
        return "Chicken:cock-a-doodle-doo";
    }//重写sound方法

    @Override
    public String howToEat() {
        return "Chicken:Fry it";
    }//重写howToEat方法
}

class Tiger extends Animal{//老虎类继承抽象的Animal类
    @Override
    public  String sound(){
        return "Tiger:RROOAARR";
    }//重写sound方法
}

abstract class Fruit implements Edible{//抽象类Fruit实现接口Edible,因为它不实现howToEat方法,所以Fruit类必须是抽象的
}

class Apple extends Fruit {//苹果类继承Fruit类(Fruit的子类必须实现howToEat方法)
    @Override
    public String howToEat(){
        return "Make apple cider";
    }//重写howToEat方法
}

class Orange extends Fruit{//橘子类继承水果类(Fruit的子类必须实现howToEat方法)
    @Override
    public String howToEat(){
        return "Orange:Make orange juice";
    }//重写howToEat方法
}

Snipaste_2020-05-02_16-43-34

Comparable 接口

Comparable 接口定义了 compareTo 方法 , 用于比较对象 。

package java.lang;	// 在java.lang包中定义了比较对象的接口
public interface Comparable<E>{
    public int compareTo(E o);
}

Comparable 接口是一个泛型接口 。 在实现该接口时 , 泛型类型 E 被替换成一种具体的类型

Snipaste_2020-05-02_21-05-55

可见,数字是可比较的 , 字符串是可比较的 , 日期也是如此

System.out.println(new Integer(3).compareTo(new Integer(5)));
System.out.println("ABC".compareTo("ABE"));
java.util.Date date1 = new java.util.Date(2013, 1, 1);
java.util.Date date2 = new java.util.Date(2012, 1, 1);
System.out.println(date1.compareTo(date2));
-1		//  因为 3 小于 5
-2		//  因为 ABC 小于 ABE
1		//  因为 datel 大于 date 2 

对字符串数组和 Biglnteger对象数组进行排序的示例

Java API 中的java.util.Arrays.sort(Object[])方法就可以使用compareTo方法来对数组中的对象进行比较和排序

import java.math.BigInteger;

public class SortComparableObjects {
    public static void main(String[] args) {
        String[] cities = {"ShanDong", "BeiJing", "AnHui", "TaiWan"};
        java.util.Arrays.sort(cities);
        for (String city : cities)
            System.out.print(city + " ");
        System.out.println();

        BigInteger[] hugeNumbers = {new BigInteger("22222222222"), new BigInteger("33333333333333"),
        new BigInteger("4444444444444444444444")};
        java.util.Arrays.sort(hugeNumbers);
        for (BigInteger number : hugeNumbers){
            System.out.print(number + " ");
        }
        System.out.println();
    }
}
AnHui BeiJing ShanDong TaiWan 
22222222222 33333333333333 4444444444444444444444 

不能使用 sort 方法来对一个 Rectangle 对象数组进行排序 , 因为 Rectangle 类没有实现接口 Comparable 。 然而 , 可以定义一个新的 Rectangle 类来实现 Comparable 。

Snipaste_2020-05-02_21-30-15

Snipaste_2020-05-02_21-30-25

Snipaste_2020-05-02_21-48-56

Snipaste_2020-05-02_22-08-33

Cloneable接口

Cloneable 接口给出了一个可克隆的对象

package java.lang;
public interface Cloneable{}

这个接口是空的 。 一个带空体的接口称为标记接口 。一个标记接口**既不包括常量也不包括方法 **。 它用来表示一个类拥有某些特定的属性。

Calender calendar = new GregorianCalender(2020, 5, 3);
Calender calendar1 = calendar;	// 将 calendar 的引用复制给 calendarl,所以calendar和calendarl 都指向相同的 Calendar 对象
Calender calendar2 = (Calendar)calendar.clone();  // 创建一个新对象,它是 calendar 的克隆,是内容相同的不同对象
System.out.println("calendar == calendar1 is " + 
                  (calendar == calendar1));
System.out.println("calendar == calendar2 is " +
                  (calendar == calendar2));
System.out.println("calendar.equals(calendar2) is " +
                  calendar.equals(calendar2));
calendar == calendar1 is true
calendar == calendar2 is false
calendar.equals(calendar2) is true

为了定义一个自定义类来实现 Cloneable 接口 , 这个类必须覆盖 Object 类中的clone()方法

public class House implements Cloneable, Comparable<House> {
    private int id;
    private double area;
    private java.util.Date whenBuilt;
    
    public House(int id, double area){
        this.id = id;
        this.area = area;
        whenBuilt = new java.util.Date();
    }
    
    public int getId(){
        return id;
    }
    
    public double getArea(){
        return area;
    }
    
    public java.util.Date getWhenBuilt(){
        return whenBuilt;
    }
    
    @Override
    public Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
    
    @Override
    public int compareTo(House o){
        if (area > o.area)
            return 1;
        else  if (area < o.area)
            return -1;
        else  
            return 0;
    }
}
  • House 类实现在 Object类中定义的 clone 方法 ,方法头是protected native Object clone() throws CloneNotSupportedException;,关键字 native 表明这个方法不是用 Java 写的 , 但它是 JVM 针对自身平台实现的。 关键字 protected 限定方法只能在同一个包内或在其子类中访问 。 由于这个原因 , House 类必须覆盖该方法并将它的可见性修饰符改为 public , 这样 , 该方法就可以在任何一个包中使用

  • 因为 Object 类中针对自身平台实现的 clone 方法完成了克隆对象的任务 , 所以 , 在House 类中的 clone 方法只要简单调用 super.clone()即可

House house1 = new House(1, 1750.50);
House house2 = (House)house1.clone(); // housel和house2是两个内容相同的不同对象

Object 类中的 clone 方法将原始对象的每个数据域复制给目标对象 。 如果一个数据域是基本类型 , 复制的就是它的值。 如果一个数据域是对象 , 复制的就是该域的引用。这称为浅复制而不是深复制

Snipaste_2020-05-03_08-46-57

改为深复制

public Object clone() throws CloneNotSupportedException{
    House houseClone = (House)super.clone();					// 浅复制
    houseClone.whenBuilt = (java.util.Date)(whenBuilt.clone());	// 深复制
    return houseClone;
}

接口与抽象类

一个类可以实现多个接口 , 但是只能继承一个父类 。

Snipaste_2020-05-03_09-02-43

  • Java 只允许为类的扩展做单一继承 , 但是允许使用接 n 做多重扩展 。
public class NewClass extends BaseClass implements Interfase1, ..., InterfaceN {...}
  • 利用关键字 extends , 接口可以继承其他接口,这种接口叫做子接口
public interface NewInterface extends Interface1, ..., InterfaceN{
    ...
}		//一个实现 Newlnterface 的类必须实现在 Newlnterface , Interfacel, ..., 			InterfaceN中定义的抽象方法

接口可以扩展其他接口而不是类 。 一个类可以扩展它的父类同时实现多个接口 。

  • 所有的类共享同一个根类 Object , 但是接口没有共同的根

实例学习:Rational类

设计一个 Rational 类 , 用于表示和处理有理数

Snipaste_2020-05-03_09-19-03

public class Rational extends Number implements Comparable<Rational> {
    private long numerator = 0;     // 分子
    private long denominator = 1;   // 分母

    public Rational(){
        this(0, 1);
    }
    public Rational(long numerator, long denominator){
        long a = Math.abs(numerator);
        long b = Math.abs(denominator);
        long gcd = gcd(a, b);
        this.numerator = ((numerator > 0) ? 1 : -1) * numerator / gcd;
        this.denominator = Math.abs(denominator) / gcd;
    }
	// 利用递归求最大公因数
    private static long gcd(long numerator, long denominator){
        if (denominator == 0) return numerator;
        else return gcd(denominator, numerator % denominator);
    }

    public long getNumerator(){
        return numerator;
    }

    public long getDenominator(){
        return  denominator;
    }

    public Rational add(Rational secondRational){
        long n = numerator * secondRational.getDenominator() +
                denominator * secondRational.getNumerator();
        long d = denominator * secondRational.getDenominator();
        return new Rational(n, d);
    }

    public Rational substract(Rational secondRational){
        long n = numerator * secondRational.getDenominator() -
                denominator * secondRational.getNumerator();
        long d = denominator * secondRational.getDenominator();
        return new Rational(n, d);
    }

    public Rational multiply(Rational secondRational){
        long n = numerator * secondRational.getNumerator();
        long d = denominator * secondRational.getDenominator();
        return new Rational(n, d);
    }

    public Rational divide(Rational secondRational){
        long n = numerator * secondRational.getDenominator();
        long d = denominator * secondRational.getNumerator();
        return new Rational(n, d);
    }

    @Override
    public String toString(){       // 继承了 Object类
        if (denominator == 1)
            return numerator + "";
        else
            return numerator + "/" + denominator;
    }
    
    @Override
    public int intValue(){          // 重写Number类中的intValue方法
        return (int)doubleValue();
    }
    
    @Override
    public float floatValue(){
        return (float)doubleValue();
    }
    
    @Override
    public double doubleValue(){
        return numerator * 1.0 / denominator;
    }
    
    @Override
    public long longValue(){
        return (long)doubleValue();
    }
    
    @Override
    public int compareTo(Rational secondRational){
        if (this.substract(secondRational).getNumerator() > 0)
            return 1;
        else if (this.substract(secondRational).getNumerator() < 0)
            return -1;
        else return 0;
    }

}
public class TestRational {
    public static void main(String[] args) {
        Rational r1 = new Rational(4, 2);
        Rational r2 = new Rational(2, 3);

        System.out.println(r1 + " + " + r2 + " = " + r1.add(r2));
        System.out.println(r1 + " - " + r2 + " = " + r1.substract(r2));
        System.out.println(r1 + " * " + r2 + " = " + r1.multiply(r2));
        System.out.println(r1 + " / " + r2 + " = " + r1.divide(r2));
        System.out.println(r2 + " is " + r2.doubleValue());
    }
}
2 + 2/3 = 8/3
2 - 2/3 = 4/3
2 * 2/3 = 4/3
2 / 2/3 = 3
2/3 is 0.6666666666666666

当使用+号将一个字符串和一个对象进行连接时,使用的是来自toString()方法的这个对象的字符串表示同这个字符串进行链接,所以,r1 + " + " + r2 + " = " + r1.add(r2)等价于r1.toString() + " + " + r2.toString() + " = " + r1.add(r2).toString()

Write by Gqq

posted @ 2020-05-03 10:01  ZHGQCN  阅读(193)  评论(0编辑  收藏  举报