自学内容网 自学内容网

【JavaSE】(6)抽象类和接口

目录

一、抽象类

1、什么是抽象类

2、抽象类的特点

3、抽象类的作用

4、抽象类示例代码

二、接口

1、什么是接口

2、接口的书写建议

3、接口的特点

4、实现多个接口

5、接口能“忘记类型”

6、接口间的继承

7、接口的应用

7.1、引用类型的比较--Comparable 和 Comparator 接口

7.2、引用类型数组排序--Comparable 和 Comparator 接口

7.3、引用类型的克隆--Clonable 接口

7.3.1、浅拷贝

7.3.2、深拷贝

三、抽象类和接口的区别

四、Object类

1、equals

2、hashCode


一、抽象类

1、什么是抽象类

        抽象类就是不能描述一个具体对象的类,比如类 Shape,它的子类可以有 Cycle、Squre等。Shape 的 draw 并不能确定子类对象的描绘,此时 Shape 就需要定义为抽象类。抽象类由 abstract 修饰。抽象类中,不能准确描述子类对象的方法 draw 叫做抽象方法,同样用 abstract 修饰。

        这样,抽象类负责声明抽象方法,而子类负责重写来实现抽象方法,子类就能准确描绘自己的 draw 了。

        因为抽象类的抽象方法并没有具体实现,所以抽象类不可以用 new 实例化

        因为抽象类要求子类必须重写抽象方法,所以子类要么重写抽象方法,要么子类也为抽象类,但最后一个子类必须要重写抽象方法。

        因为抽象方法必须被重写,就不能用 private、static、final 修饰了。

         抽象方法必须在抽象类中,但抽象类不一定有抽象方法。

2、抽象类的特点

        首先,它与普通类的相似之处:都可以有属性、普通方法、构造方法

        然后,它的独特性:不能实例化、可以有抽象方法。

3、抽象类的作用

        有人会说,普通类的子类也能重写,为什么非要声明抽象?实际上,abstract 起了一个编译器检查的作用,避免应该用子类(比如 Cycle)描述 draw,却误用父类(Shape)描述 draw的情况。有了 abstract ,实例化抽象父类会报错,而实例化普通父类却不会。

4、抽象类示例代码

二、接口

1、什么是接口

        接口就是通用的规范,实现规范的要求,就能使用了。接口的修饰词 interface 替代类 class 的位置。

2、接口的书写建议

  • 接口名以 I 开头。
  • 接口名最好是形容词。
  • 接口里边的成员不要加修饰符,用默认的,更简洁。

3、接口的特点

        接口不能实例化。

        一个类要实现接口,用 implememts

        一个类实现接口,要重写抽象方法,方法的修饰词只能是 public,因为重写方法的访问权限不能低于被重写方法的访问权限。如果没重写要用 abstract 修饰。

        接口中的成员变量默认且必须修饰为 public static final

        接口中的成员方法,如果不实现默认且必须public abstract

        接口中的成员方法,要实现必须 static 或者 default 修饰。默认public 修饰。

        接口中不能有静态代码块构造方法

        接口不是类,编译完的字节码也单独为一个 .class 文件。

4、实现多个接口

        一个类不能继承多个父类,但是能实现多个接口,这可以弥补继承的缺陷。

        示例代码:比如不是所有的动物都会游泳,因此不能把跑、游全部写在 Animal 类里。如果把跑、游分别写在两个类里,又不能继承多个类。这种情况,就使用接口,接口可以实现多个。

5、接口能“忘记类型”

        接口相对于继承,还有一个优势,就是接口可以“忘记类型”,而类只能“半忘记类型”。

        比如,在继承实现多态时,Dog、Cat 同属 Animal 的子类,那么只能由 Animal 的子类向上转型,而不能是其它类的子类,或者没有继承的类。

        在接口实现多态时,不管属不属于 Animal ,只要是实现了 IRunable 的类,都能够向 IRunable 转型。

        示例代码:Dog、Dock、Robot 都能跑。

6、接口间的继承

        相当于把接口合并。比如鸭子是又会跑,又会游的两栖动物,那么可以把 IRunnable 和 ISwimable 合并成一个接口。类和类单继承,类和接口多实现,接口和接口能多继承

        示例代码:

7、接口的应用

7.1、引用类型的比较--Comparable 和 Comparator 接口

        基本数据类型,可以直接比较大小。引用类型,比如自定义的 Student 不能直接比较大小,因为没有比较的标准,姓名?年龄?分数?

        因此,可以实现 Comparable 接口,并实现 compareTo 方法参数接收一个

        但这样又有个问题,固定了 age 作为比较的标准。如果一开始用的 age,过了几天业务变了,又想用 score,直接在重写的 compareTo 方法里改,是万万不可的。之前用你写的类,以 age 为标准的同事怎么办?直接改会全部乱套。对类具有侵入性(引入该组件后,其它代码要做相应的更改,以适应组件)。

        比较好的办法是,给每一种比较标准实现一个比较器,它们都实现 Comparator 接口,重写 compare 方法参数接收两个

        注意,name的类型 String 也是引用类型,得用 String 类重写的 CompareTo 。

7.2、引用类型数组排序--Comparable 和 Comparator 接口

        引用类型有了比较标准,就能进行排序了。

        用 Arrays.sort(默认升序),可以用 Student 内默认的 compareTo 比较:

        也可以指定比较器(按 name)比较:

        降序,将重写 compareTo 或者 compare 的两个值反过来

        原理,比如自定义一个冒泡排序:改动前,o1-o2>0 才交换,即升序;改动后,o2-o1>0才交换,即降序。

        降序,如果不改动 compareTo 或者 compare,也可以改动自定义冒泡排序的 > 为 <

7.3、引用类型的克隆--Clonable 接口

        Object 类是所有类的祖先,因此其它的引用的对象可以使用父类 Object 实现的方法 Clone。但是,需要满足3个条件:

(1)Object 类中的 Clone 方法访问权限范围protected,也就是如果在不同包,只能被子类使用。

        现在有一个 Person 类:

        在另一个 Test 类中创建 Person 的对象,并直接克隆,不可行。因为用 Object 类的方法克隆 Person 的对象,那么 Person 才是 Object 的子类,而不是 Test。又由于 protected 修饰,Person 的对象的克隆,就只能在 Person 类中实现,而不能在其它类中实现。

        Person 类重写 Clone 方法,让其他类间接克隆 Person 的对象:

(2)Clone 方法的返回值是 Object 类型,需要向下转型,才能实现赋值给 Person 对象:

        但仍然报错:不支持克隆。

(3)Person 类必须实现 Cloneable 接口。Cloneable 接口是一个空接口,主要是标记作用,标记这个类能克隆。

7.3.1、浅拷贝

        现在,我们实现了 Person 对象的克隆,给 Person 加一个 Money 对象作为成员变量

        测试代码:

        运行结果:

        代码只改变了 person2 的 money,事实上 person1 的 money 也改变了

        绘制数据存储图:

        m  也是引用类型,存的是对象的地址。只创建了新的 Person 对象空间,并复制内容;而没有创建新的 Money 对象空间,并复制内容。这就是浅拷贝

7.3.2、深拷贝

        深拷贝,必须把 Person 类里面的所有引用类型成员变量也拷贝一份。

        再运行上面的测试代码,person1的 m 的 money就不会跟着改值了:

三、抽象类和接口的区别

        抽象类其实有普通类的功能。

  • 抽象类可以有普通成员,构造方法;接口只能有抽象方法,想有成员变量和方法,得有修饰符的限制。
  • 抽象类是被继承,接口是被实现。
  • 一个类只能继承一个抽象类,但能实现多个接口。
  • 一个接口不能继承普通类,但是能继承多个接口,相当于把继承的接口合并。
  • 抽象类可以的访问权限跟普通类一样,默认和 public;但接口只能是 public。

四、Object类

        Object类是所有类的爸爸,很重要。

1、equals

        重点是比较相不相等(返回值是 boolean)compare 和 compareTo比较哪个大(返回值是 int)

        不是我们想要的比较值,所以重写

2、hashCode

        作用是算一个对象在内存中的位置,详细的后续学。源码是 native 方法,看不到具体实现,

        两个地址不同的对象,就算赋值相同,分别直接用 hashCode,hashcode值也不一样。

        这样改了是相同的。


原文地址:https://blog.csdn.net/2401_86272648/article/details/143833856

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!