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();
}
-
抽象类和常规类很像 , 但是不能使用 new 操作符创建它的实例 。 抽象方法只有定义而没有实现 。 它的实现由子类提供 。 一个包含抽象方法的类必须声明为抽象类
-
抽象类的构造方法定义为 Protected , 因为它只被子类使用 。 创建一个具体子类的实例时 , 它的父类的构造方法被调用以初始化父类中定义的数据域
为何要使用抽象方法
JVM 在运行时根据对象的类型动态地决定调用哪一个方法
抽象类的几点说明
实例学习:抽象的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方法
}
Comparable 接口
Comparable 接口定义了 compareTo 方法 , 用于比较对象 。
package java.lang; // 在java.lang包中定义了比较对象的接口
public interface Comparable<E>{
public int compareTo(E o);
}
Comparable 接口
是一个泛型接口
。 在实现该接口时 , 泛型类型 E
被替换成一种具体的类型
可见,数字是可比较的 , 字符串是可比较的 , 日期也是如此
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-25](https://tva3.sinaimg.cn/large/005tpOh1gy1geefh3rocsj30kw02vwfg.jpg)
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 方法将原始对象的每个数据域复制给目标对象 。 如果一个数据域是基本类型 , 复制的就是它的值。 如果一个数据域是对象 , 复制的就是该域的引用。这称为浅复制
而不是深复制
。
改为深复制
public Object clone() throws CloneNotSupportedException{
House houseClone = (House)super.clone(); // 浅复制
houseClone.whenBuilt = (java.util.Date)(whenBuilt.clone()); // 深复制
return houseClone;
}
接口与抽象类
一个类可以实现多个接口 , 但是只能继承一个父类 。
- Java 只允许为类的扩展做单一继承 , 但是允许使用接 n 做多重扩展 。
public class NewClass extends BaseClass implements Interfase1, ..., InterfaceN {...}
- 利用关键字 extends , 接口可以继承其他接口,这种接口叫做
子接口
public interface NewInterface extends Interface1, ..., InterfaceN{
...
} //一个实现 Newlnterface 的类必须实现在 Newlnterface , Interfacel, ..., InterfaceN中定义的抽象方法
接口可以扩展其他接口而不是类 。 一个类可以扩展它的父类同时实现多个接口 。
- 所有的类共享同一个
根类 Object
, 但是接口没有共同的根
实例学习:Rational类
设计一个 Rational 类 , 用于表示和处理有理数
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