Java面向对象(八)
Java面向对象(八)
二十四、abstract 关键字
- abstract 可以用来修饰的结构:类、方法。
- abstract 修饰类:抽象类。
- 此类不能实例化。
- 抽象类中一定有构造器,便于子类实例化时调用。(涉及:子类对象实例化的全过程)
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作。
- abstract 修饰方法:抽象方法。
- 抽象方法只有方法的声明,没有方法体。
- 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
- 若子类重写了父类中的所有的抽象方法后,此子类方可实例化。若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰。
- 注意:
- abstract 不能用来修饰:属性、构造器等结构。
- abstract 不能用来修饰私有方法、静态方法、final 的方法、final 的类。
创建抽象类的匿名子类对象
public class PersonTest {
public static void main(String[] args) {
//非匿名的类非匿名的对象
Worker worker = new Worker();
method1(worker);
//非匿名的类匿名的对象
method1(new Worker());
//创建了一匿名子类的非匿名对象:p
Person p = new Person(){
@Override
public void eat() {
System.out.println("吃东西");
}
@Override
public void breath() {
System.out.println("好好呼吸");
}
};
method1(p);
//创建匿名子类的匿名对象
method1(new Person(){
@Override
public void eat() {
System.out.println("吃好吃东西");
}
@Override
public void breath() {
System.out.println("好好呼吸新鲜空气");
}
});
}
public static void method1(Person p){
p.eat();
p.breath();
}
}
class Worker extends Person{
@Override
public void eat() {
}
@Override
public void breath() {
}
}
二十五、模板方法设计模式(TemplateMethod)
-
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
-
解决的问题:
当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
-
例子:
public class TemplateTest {
public static void main(String[] args) {
SubTemplate t = new SubTemplate();
t.spendTime();
}
}
abstract class Template{
//计算某段代码执行所需要花费的时间
public void spendTime(){
long start = System.currentTimeMillis();
this.code();//不确定的部分、易变的部分
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));
}
public abstract void code();
}
class SubTemplate extends Template{
@Override
public void code() {
for(int i = 2;i <= 1000;i++){
boolean isFlag = true;
for(int j = 2;j <= Math.sqrt(i);j++){
if(i % j == 0){
isFlag = false;
break;
}
}
if(isFlag){
System.out.println(i);
}
}
}
}
- 常用场景:
- 数据库访问的封装
- Junit 单元测试
- JavaWeb 的 Servlet 中关于 doGet/doPost 方法调用
- Hibernate 中模板程序
- Spring 中 JDBCTemlate、HibernateTemplate 等
二十六、接口(interface)
26.1 接口简介:
-
接口使用关键字 interface 来定义。
-
Java中,接口和类是并列的两个结构。主要解决 Java 中不能实现多重继承的(即一个子类不能同时继承于多个父类)问题。
26.2 接口的定义和实现:
-
JDK7 及以前:只能定义全局常量和抽象方法。
-
全局常量:public static final 。但是书写时,可以省略不写。
-
抽象方法:public abstract 。也可省略不写。
-
-
JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
-
接口中不能定义构造器的,意味着接口不可以实例化。
-
Java开发中,接口通过让类去实现(implements)的方式来使用。
-
如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化。
-
如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类。
-
interface i {
// 全局变量
public static final int MAX_NUM = 100; //完整写法
int MIN_NUM = 1; //省略了public static final
// 抽象方法
public abstract void show();
//省略了public abstract
void change();
}
class I implements i {
@Override
public void show() {
System.out.println("调用i接口的show方法");
}
@Override
public void change() {
System.out.println("调用i接口的change方法");
}
}
// 实现类没有覆盖接口中所有的抽象方法
abstract class I2 implements i{
@Override
public void show() {
}
}
public class InterfaceTest {
public static void main(String[] args) {
System.out.println(i.MAX_NUM);
System.out.println(i.MIN_NUM);
// i.MIN_NUM = 2;
I i2 = new I();
i2.show();
}
}
-
Java类可以实现多个接口 ---> 弥补了Java单继承性的局限性
定义Java类的语法格式:先写extends,后写implements
格式:class AA extends BB implements CC,DD,EE
-
接口与接口之间可以继承,而且可以多继承。
class Bullet extends Object implements CC,DD{
@Override
public void method1() {
// TODO Auto-generated method stub
}
@Override
public void method2() {
// TODO Auto-generated method stub
}
interface AA{
void method1();
}
interface BB{
void method2();
}
interface CC extends AA,BB{
}
interface DD{
}
26.3 接口的使用:
-
接口的具体使用,体现多态性。
-
接口的主要用途就是被实现类实现。(面向接口编程)
-
接口,实际上可以看做是一种规范。
-
举例:JDBC
public class USBTest {
public static void main(String[] args) {
Computer com = new Computer();
Flash flash = new Flash();
com.transferData(flash);
}
}
class Computer{
public void transferData(USB usb){ // USB usb = new Flash();
usb.start();
System.out.println("具体传输数据的细节");
usb.stop();
}
}
interface USB{
//常量:定义了长、宽、最大最小的传输速度等
void start();
void stop();
}
class Flash implements USB{
@Override
public void start() {
System.out.println("U盘开启工作");
}
@Override
public void stop() {
System.out.println("U盘结束工作");
}
}
class Printer implements USB{
@Override
public void start() {
System.out.println("打印机开启工作");
}
@Override
public void stop() {
System.out.println("打印机结束工作");
}
}
26.4 接口的实现类的对象创建(例子)
public class USBTest {
public static void main(String[] args) {
Computer com = new Computer();
//1.创建了接口的非匿名实现类的非匿名对象
Flash flash = new Flash();
com.transferData(flash);
//2. 创建了接口的非匿名实现类的匿名对象
com.transferData(new Printer());
//3. 创建了接口的匿名实现类的非匿名对象
USB phone = new USB(){
@Override
public void start() {
System.out.println("手机开始工作");
}
@Override
public void stop() {
System.out.println("手机结束工作");
}
};
com.transferData(phone);
//4. 创建了接口的匿名实现类的匿名对象
com.transferData(new USB(){
@Override
public void start() {
System.out.println("mp3开始工作");
}
@Override
public void stop() {
System.out.println("mp3结束工作");
}
});
}
}
class Computer{
public void transferData(USB usb){//USB usb = new Flash();
usb.start();
System.out.println("具体传输数据的细节");
usb.stop();
}
}
interface USB{
//常量:定义了长、宽、最大最小的传输速度等
void start();
void stop();
}
class Flash implements USB{
@Override
public void start() {
System.out.println("U盘开启工作");
}
@Override
public void stop() {
System.out.println("U盘结束工作");
}
}
class Printer implements USB{
@Override
public void start() {
System.out.println("打印机开启工作");
}
@Override
public void stop() {
System.out.println("打印机结束工作");
}
}
26.5 接口应用:代理模式(Proxy)
-
概述: 代理模式是Java开发中使用较多的一种设计模式。
代理设计就是为其他对象提供一种代理以控制对这个对象的访问。
public class StaticProxyTest {
public static void main(String[] args) {
Star s = new Proxy(new RealStar());
s.confer();
s.signContract();
s.bookTicket();
s.sing();
s.collectMoney();
}
}
interface Star {
void confer();// 面谈
void signContract();// 签合同
void bookTicket();// 订票
void sing();// 唱歌
void collectMoney();// 收钱
}
class RealStar implements Star { // 被代理类
public void confer() {
}
public void signContract() {
}
public void bookTicket() {
}
public void sing() {
System.out.println("明星:歌唱~~~");
}
public void collectMoney() {
}
}
class Proxy implements Star { // 代理类
private Star real;
public Proxy(Star real) {
this.real = real;
}
public void confer() {
System.out.println("经纪人面谈");
}
public void signContract() {
System.out.println("经纪人签合同");
}
public void bookTicket() {
System.out.println("经纪人订票");
}
public void sing() {
real.sing();
}
public void collectMoney() {
System.out.println("经纪人收钱");
}
}
-
应用场景:
-
安全代理:屏蔽对真实角色的直接访问。
-
远程代理:通过代理类处理远程方法调用(RMI)
-
延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。
-
-
分类 :
- 静态代理(静态定义代理类)
- 动态代理(动态生成代理类):JDK自带的动态代理,需要反射等知识
26.6 特殊例子:
example one:
interface A {
int x = 0;
}
class B {
int x = 1;
}
class C extends B implements A {
public void pX() {
// The field x is ambiguous(编译不通过。因为x是不明确的)
// System.out.println(x);
System.out.println(super.x);// 1
System.out.println(A.x);// 0,全局常量
}
public static void main(String[] args) {
new C().pX();
}
}
example two:
interface Playable {
void play();
}
interface Bounceable {
void play();
}
interface Rollable extends Playable,
Bounceable {
Ball ball = new Ball("PingPang");
}
class Ball implements Rollable {
private String name;
public String getName() {
return name;
}
public Ball(String name) {
this.name = name;
}
public void play() {
ball = new Ball("Football"); // 编译报错,在接口中已经定义了ball对象,所以该对象为 public static final,此处违反 final 不能重新赋值
System.out.println(ball.getName());
}
}
26.7 Java8 接口新特性
-
JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法。
- 静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。
- 默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。 我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。
-
注意点:
(1)接口中定义的静态方法,只能通过接口来调用。(2)通过实现类的对象,可以调用接口中的默认方法。如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法。
(3)如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则
(4)如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错。-->接口冲突。需要在实现类重写该方法。
(5)在子类(或实现类)的方法中调用父类、接口中被重写的方法。
- 调用自己重写的方法:方法名();
- 调用父类的方法:super.方法名();
- 调用接口的默认方法:接口名.super.方法名();
例子:
public interface CompareA {
//静态方法
public static void method1(){
System.out.println("CompareA:北京");
}
//默认方法
public default void method2(){
System.out.println("CompareA:上海");
}
default void method3(){
System.out.println("CompareA:上海");
}
}
public interface CompareB {
default void method3(){
System.out.println("CompareB:上海");
}
}
public class SuperClass {
public void method3(){
System.out.println("SuperClass:北京");
}
}
public class SubClassTest {
public static void main(String[] args) {
SubClass s = new SubClass();
// s.method1();
// SubClass.method1();
//知识点1:接口中定义的静态方法,只能通过接口来调用。
CompareA.method1();
//知识点2:通过实现类的对象,可以调用接口中的默认方法。
//如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
s.method2();
//知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,
//那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则
//知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
//那么在实现类没有重写此方法的情况下,报错。-->接口冲突。
//这就需要我们必须在实现类中重写此方法
s.method3();
}
}
class SubClass extends SuperClass implements CompareA,CompareB{
public void method2(){
System.out.println("SubClass:上海");
}
public void method3(){
System.out.println("SubClass:深圳");
}
//知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
public void myMethod(){
method3();//调用自己定义的重写的方法
super.method3();//调用的是父类中声明的
//调用接口中的默认方法
CompareA.super.method3();
CompareB.super.method3();
}
}
26.8 接口与抽象类的区别
NO. | 区别点 | 抽象类 | 接口 |
---|---|---|---|
1 | 定义 | 包含抽象方法的类 | 主要是抽象方法和全局常量的集合 |
2 | 组成 | 构造方法、抽象方法、普通方法、 常量、变量 | 常量、抽象方法、(jdk8.0:默认方法、静态方法) |
3 | 使用 | 子类继承抽象类(extends) | 子类实现接口(implements) |
4 | 关系 | 抽象类可以实现多个接口 | 接口不能继承抽象类,但允许继承多个接口 |
5 | 常见设计模式 | 模板方法 | 简单工厂、工厂方法、代理模式 |
6 | 对象 | 都通过对象的多态性产生实例化对象 | |
7 | 局限 | 抽象类有单继承的局限 | 接口没有此局限 |
8 | 实际 | 作为一个模板 | 是作为一个标准或是表示一种能力 |
9 | 选择 | 如果抽象类和接口都可以使用的话,优先使用接口,因为避免单继承的局 |
- 在开发中,常看到一个类不是去继承一个已经实现好的类,而是要么继承抽象类, 要么实现接口。