抽象类与抽象方法(abstract关键字的使用)
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一 般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父 类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
用abstract关键字来修饰一个类,这个类叫做抽象类。
此类不能实例化
抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
开发中,都会提供抽象类的子类,让子类对象实例化
用abstract来修饰一个方法,该方法叫做抽象方法。
抽象方法只有方法的声明,没有方法体。
public abstract void eat();
包含抽象方法的类,一定是一个抽象类,因为抽象方法无法调用
反之,抽象类中可以没有抽象方法
若子类重写了父类中所有的抽象方法,则子类可实例化。
若子类没有重写了父类中所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
示例代码:
package com.xxx.java;
public class AbstractTest {
public static void main(String[] args) {
// 一旦类抽象了,就不可实例化了
// Person p1 = new Person();
// p1.eat();
}
}
abstract class Creature{
public abstract void breath();
}
abstract class Person extends Creature {
String name;
int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public void walk() {
System.out.println("人走路");
}
// 不是抽象方法
// public void eat() {
//
// }
// 抽象方法
public abstract void eat();
}
class Student extends Person {
public Student() {
super();
}
public Student(String name, int age) {
super(name, age);
}
@Override
public void breath() {
System.out.println("学生呼吸新鲜空气");
}
public void walk() {
System.out.println("学生走路");
}
public void eat() {
System.out.println("学生应该吃有营养的食物");
}
}
应用代码:
之前的练习:几何图像
父类的findArea()因为无法确定图像是什么,所有无法确定面积,之前我们将其返回值设为0.0,并在各个子类中重写该方法
修改:
package com.xxx.exer;
public abstract class GeometricObject {
protected String color;
protected double weight;
public GeometricObject(String color, double weight) {
super();
this.color = color;
this.weight = weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public abstract double findArea() ;
}
abstract使用上的注意点:
abstract无法修饰:属性、构造器、代码块、final的类等。
abstract不能用来修饰私有方法、静态方法、final的方法。
练习:
编写一个Employee类,声明为抽象类,
包含如下三个属性:name,id,salary。
提供必要的构造器和抽象方法:work()。
对于Manager类来说,他既是员工,还具有奖金(bonus)的属性。
请使用继承的思想,设计CommonEmployee类和Manager类,要求类 中提供必要的方法进行属性访问。
package com.xxx.exer1;
public abstract class Employee {
private String name;
private int id;
private double salary;
public Employee() {
super();
}
public Employee(String name, int id, double salary) {
super();
this.name = name;
this.id = id;
this.salary = salary;
}
public abstract void work();
}
package com.xxx.exer1;
public class Manage extends Employee {
private double bonus;
public Manage(double bonus) {
super();
this.bonus = bonus;
}
public Manage(String name, int id, double salary, double bonus) {
super(name, id, salary);
this.bonus = bonus;
}
@Override
public void work() {
System.out.println("管理员工,提高生产效率");
}
}
package com.xxx.exer1;
public class CommonEmployee extends Employee {
@Override
public void work() {
System.out.println("员工在一线车间生产产品");
}
}
package com.xxx.exer1;
public class EmployeeTest {
public static void main(String[] args) {
Employee manager = new Manage("Alkaid",1001,5000,50000);
manager.work();
CommonEmployee commonEmployee = new CommonEmployee();
commonEmployee.work();
}
}
抽象类的匿名子类
其中的Person和Student类在示例代码中
package com.xxx.java;
public class PersonTest {
public static void main(String[] args) {
method(new Student()); // 匿名对象
Worker worker = new Worker();
method1(worker);//非匿名类的非匿名对象
method1(new Worker());//非匿名类的匿名对象
System.out.println("*************");
//创建了匿名子类的对象:p
Person p = new Person() {
@Override
public void eat() {
System.out.println("吃東西");
}
@Override
public void breath() {
System.out.println("好好呼吸");
}
};
method1(p);
System.out.println("*************");
//创建了匿名子类的匿名对象
method1(new Person() {
@Override
public void eat() {
System.out.println("吃好吃的东西");
}
@Override
public void breath() {
System.out.println("好好呼吸新鲜的空气");
}
});
}
public static void method(Student s) {
}
public static void method1(Person p) {
p.eat();
p.breath();
}
}
class Worker extends Person {
@Override
public void eat() {
System.out.println("工人吃饭");
}
@Override
public void breath() {
System.out.println("工人呼吸");
}
}
多态的应用:模板方法设计模式(TemplateMethod)
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象 类的行为方式。
解决的问题:
当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
模板方法设计模式是编程中经常用得到的模式。各个框架、类库中都有他的 影子,比如常见的有:
数据库访问的封装
Junit单元测试
JavaWeb的Servlet中关于doGet/doPost方法调用
Hibernate中模板程序
Spring中JDBCTemlate、HibernateTemplate等
代码示例:
1
package com.xxx.java;
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();
//不确定部分
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);
}
}
}
}
}
2
package com.xxx.java;
//抽象类的应用:模板方法的设计模式
public class TemplateMethodTest {
public static void main(String[] args) {
BankTemplateMethod btm = new DrawMoney();
btm.process();
BankTemplateMethod btm2 = new ManageMoney();
btm2.process();
}
}
abstract class BankTemplateMethod {
// 具体方法
public void takeNumber() {
System.out.println("取号排队");
}
public abstract void transact(); // 办理具体的业务 //钩子方法
public void evaluate() {
System.out.println("反馈评分");
}
// 模板方法,把基本操作组合到一起,子类一般不能重写
public final void process() {
this.takeNumber();
this.transact();// 像个钩子,具体执行时,挂哪个子类,就执行哪个子类的实现代码
this.evaluate();
}
}
class DrawMoney extends BankTemplateMethod {
public void transact() {
System.out.println("我要取款!!!");
}
}
class ManageMoney extends BankTemplateMethod {
public void transact() {
System.out.println("我要理财!我这里有2000万美元!!");
}
}
练习
编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个 Employee对象的生日,则将该雇员的工资增加100元。
实验说明:
(1)定义一个Employee类,该类包含:
private成员变量name,number,birthday,其中birthday 为MyDate类的对象; abstract方法earnings();
toString()方法输出对象的name,number和birthday。
(2)MyDate类包含:
private成员变量year,month,day ;
toDateString()方法返回日期对应的字符串:xxxx年xx月xx日
(3)定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处理。该类包括:private成员变量monthlySalary;
实现父类的抽象方法earnings(),该方法返回monthlySalary值;toString()方法输 出员工类型信息及员工的name,number,birthday。
(4)参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的 员工处理。该类包括:
private成员变量wage和hour;
实现父类的抽象方法earnings(),该方法返回wage*hour值;
toString()方法输出员工类型信息及员工的name,number,birthday。
(5)定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各 类雇员对象的引用。利用循环结构遍历数组元素,输出各个对象的类型,name,number,birthday,以及该对象生日。当键盘输入本月月份值时,如果本 月是某个Employee对象的生日,还要输出增加工资信息。
提示:
//定义People类型的数组People c1[]=new People[10];
//数组元素赋值
c1[0]=new People("John","0001",20);
c1[1]=new People("Bob","0002",19);
//若People有两个子类Student和Officer,则数组元素赋值时,可以使父类类型的数组元素指向子类。
c1[0]=new Student("John","0001",20,85.0);
c1[1]=new Officer("Bob","0002",19,90.5);
代码
package com.xxx.exer2;
public abstract class Employee {
private String name;
private int number;
private MyDate birthday;
public Employee(String name, int number, MyDate birthday) {
super();
this.name = name;
this.number = number;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "[name=" + name + ", number=" + number + ", birthday=" + birthday.toDateString();
}
public abstract double earnings();
}
package com.xxx.exer2;
public class MyDate {
private int year;
private int month;
private int day;
public MyDate(int year, int month, int day) {
super();
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public String toDateString() {
return year + "年" + month + "月" + day + "日";
}
}
package com.xxx.exer2;
public class HourlyEmployee extends Employee {
private int wage; // 每小时的工作
private int hour; // 月工作的小时数
public HourlyEmployee(String name, int number, MyDate birthday) {
super(name, number, birthday);
}
public HourlyEmployee(String name, int number, MyDate birthday,int wage,int hour) {
super(name, number, birthday);
this.wage = wage;
this.hour = hour;
}
@Override
public double earnings() {
return wage * hour;
}
@Override
public String toString() {
return "HourlyEmployee [" + super.toString() + "]";
}
}
package com.xxx.exer2;
public class SalariedEmployee extends Employee{
double monthlySalary; //月工资
public SalariedEmployee(String name, int number, MyDate birthday) {
super(name, number, birthday);
}
public SalariedEmployee(String name, int number, MyDate birthday,double monthlySalary) {
super(name, number, birthday);
this.monthlySalary = monthlySalary;
}
public double getMonthlySalary() {
return monthlySalary;
}
public void setMonthlySalary(double monthlySalary) {
this.monthlySalary = monthlySalary;
}
@Override
public double earnings() {
return monthlySalary;
}
@Override
public String toString() {
return "SalariedEmployee [" + super.toString() + "]";
}
}
测试:
package com.xxx.exer2;
import java.util.Calendar;
import java.util.Scanner;
public class PayrollSystem {
public static void main(String[] args) {
//方式一
// Scanner scan = new Scanner(System.in);
// System.out.println("请输入当前的月份:");
// int month = scan.nextInt();
//方式二
Calendar calendar = Calendar.getInstance();
int month = calendar.get(Calendar.MONTH);
System.out.println(month);
Employee[] emps = new Employee[2];
emps[0] = new SalariedEmployee("Tom",1002,new MyDate(1992,2,28),10000);
emps[1] = new HourlyEmployee("Mike",1003,new MyDate(1999,8,28),50,240);
for (int i = 0; i < emps.length; i++) {
System.out.println(emps[i]);
double salary = emps[i].earnings();
System.out.println("月工资为" + salary);
if(month++ == emps[i].getBirthday().getMonth()) {
System.out.println("生日快乐,奖励100元");
}
}
}
}
接口(interface)
一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方 法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又 没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打 印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都 支持USB连接。
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则 必须能...”的思想。继承是一个"是不是"的关系,而接口实现则是 "能不能" 的关系。
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守
接口的使用体现了多态性
接口实际上定义了一种规范
体会面向接口编程
在java中,接口和类是并列的两个结构
接口中不能定义构造器,意味着接口不可以实例化。
接口通过让类去实现(implements)的方式来使用
如果实现类实现(类的重写)了接口中的所有抽象方法,则此实现类就可以实例化
如果实现类没有实现(类的重写)了接口中的所有抽象方法,则此实现类仍为一个抽象类
接口采用多继承机制。
类可以实现多个接口
接口与接口之间可以多继承
抽象类也可以实现接口,抽象类可以继承非抽象的类
接口的使用上也满足多态性
书写格式:
//类实现接口
class Bullet extends Object implements Flyable,Attackable{
//接口或父类定义的抽象方法
}
//接口继承接口
interface CC extends AA,BB{
//代码块
}
如何定义接口中的成员
JDK7以前:只能定义全局常量和抽象方法
全局常量:public static final的,但是书写时可以省略不写public static final
抽象方法:public abstract的,但是书写时可以省略不写public abstract
JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认(不是缺省)方法
静态方法:接口中定义的静态方法只能通过接口来调用
默认方法:
通过实现类的实例对象,可以调用接口中的默认方法
如果实现类重写了接口中的默认方法,调用时,调用的是重写后的方法
若子类(实现类)继承的父类和实现的接口中声明了同名同参数的方法,子类在没有重写此方法的情况下,默认调用的是父类中的方法——类优先原则
如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么实现类没有重写此方法的情况下,报错——接口冲突——必须在实现类中重写此方法
可以通过接口名.super.方法名调用接口中的默认方法
面试题:抽象类和接口有哪些异同?
同:
都不能实例化
都可以被继承
异:
抽象类有构造器,接口不能声明构造器
接口可以多继承,抽象类只能单继承
JAVA 7
使用示例
package com.xxx.java1;
public class InterfaceTest {
public static void main(String[] args) {
System.out.println(Flyable.MAX_SPEED);
System.out.println(Flyable.MIN_SPEED);
// Flyable.MIN_SPEED = 10;
System.out.println("***************");
Plane plane = new Plane();
plane.fly();
}
}
interface Flyable {
// 全局常量
public static final int MAX_SPEED = 7900; // 第一宇宙速度
// 书写时可以省略不写public static final
int MIN_SPEED = 1; // 第一宇宙速度
public abstract void fly();
// 书写时可以省略不写public abstract
void stop();
}
interface Attackable {
void sttack();
}
class Plane implements Flyable {
@Override
public void fly() {
System.out.println("飞机通过引擎起飞");
}
@Override
public void stop() {
System.out.println("驾驶员减速停止");
}
}
abstract class Kite implements Flyable {
@Override
public abstract void fly();
}
class Bullet extends Object implements CC, Flyable, Attackable {
@Override
public void sttack() {
}
@Override
public void fly() {
}
@Override
public void stop() {
}
@Override
public void method1() {
}
@Override
public void method2() {
}
}
//**********************************************
interface AA {
void method1();
}
interface BB {
void method2();
}
interface CC extends AA, BB {
}
应用举例:
package com.xxx.java1;
public class USBTest {
public static void main(String[] args) {
Computer computer = new Computer();
// 体现了接口的多态性
// 1.创建了接口的非匿名实现类的非匿名对象
Flash flash = new Flash();
computer.transferDate(flash);
// 2.创建接口的非匿名实现类的匿名对象
computer.transferDate(new Printer());
// 3.创建接口的匿名实现类的非匿名对象
USB phone = new USB() {
@Override
public void start() {
System.out.println("手机开始工作");
}
@Override
public void stop() {
System.out.println("手机停止工作");
}
};
computer.transferDate(phone);
// 4.创建接口的匿名实现类的匿名对象
computer.transferDate(new USB() {
@Override
public void start() {
System.out.println("mp4开始工作");
}
@Override
public void stop() {
System.out.println("mp4停止工作");
}
});
}
}
interface USB {
// 常量:定义了长、宽、最大最小的传输速度等
void start();
void stop();
}
class Computer {
public void transferDate(USB usb) {
usb.start();
System.out.println("具体的传输数据的细节");
usb.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("打印机结束工作");
}
}
JAVA8
使用示例
package com.xxx.java8;
public interface CompareA {
//静态方法
public static void method1() {
System.out.println("method1:北京");
}
//默认方法
public default void method2() {
System.out.println("method2:上海");
}
//省略了public修饰符
//default不是权限修饰符 缺省!
default void method3() {
System.out.println("method3:上海");
}
}
package com.xxx.java8;
public interface CompareB {
default void method3() {
System.out.println("method3:南京");
}
}
package com.xxx.java8;
public class SuperClass {
public void method3() {
System.out.println("SuperClass:南京");
}
}
测试
package com.xxx.java8;
public class SubclassTest {
public static void main(String[] args) {
SubClass1 s1 = new SubClass1();
//接口中定义的静态方法只能通过接口来调用
CompareA.method1();
//s.method1();
//通过实现类的实例对象,可以调用接口中的默认方法
//如果实现类重写了接口中的默认方法,调用时,调用的是重写后的方法
s1.method2();
//若子类(实现类)继承的父类和实现的接口中声明了同名同参数的方法,子类在没有重写此方法的情况下,默认调用的是父类中的方法——类优先原则
s1.method3();
SubClass2 s2 = new SubClass2();
//如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么实现类没有重写此方法的情况下,报错——接口冲突
//必须在实现类中重写此方法
s2.method3();
}
}
class SubClass1 extends SuperClass implements CompareA {
public void method2() {
System.out.println("SubClass:上海");
}
}
class SubClass2 implements CompareA,CompareB {
public void method2() {
System.out.println("SubClass:上海");
}
@Override
public void method3() {
System.out.println("必须重写");
}
public void myMethod() {
//调用接口中的默认方法
CompareA.super.method2();
}
}
应用举例
package com.xxx.java8;
interface Filial {// 孝顺的
default void help() {
System.out.println("老妈,我来救你了");
}
}
interface Spoony {// 痴情的
default void help() {
System.out.println("媳妇,别怕,我来了");
}
}
class Father {
public void help() {
System.out.println("儿子,救我媳妇!");
}
}
public class Man extends Father implements Filial,Spoony {
public void help() {
System.out.println("我两个都救");
Filial.super.help();
Spoony.super.help();
}
public static void main(String[] args) {
new Man().help();
}
}
接口的设计模式:代理模式(Proxy)
概述:
代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。
应用场景:
安全代理:屏蔽对真实角色的直接访问。
远程代理:通过代理类处理远程方法调用(RMI)
延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象,比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有 100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。
分类
静态代理(静态定义代理类)
动态代理(动态生成代理类)
JDK自带的动态代理,需要反射等知识
使用示例:
1
package com.xxx.java1;
public class NetworkTest {
public static void main(String[] args) {
new ProxyServer(new Server()).browse();
}
}
interface Network{
void browse();
}
//被代理类
class Server implements Network{
@Override
public void browse() {
System.out.println("真实服务器访问网络");
}
}
//代理类
class ProxyServer implements Network{
private Network work;
public ProxyServer(Network work) {
super();
this.work = work;
}
public void check() {
System.out.println("联网之前的检查工作");
}
@Override
public void browse() {
check();
work.browse();
}
}
2
package com.xxx.java1;
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("经纪人收钱");
}
}
接口的设计模式:工厂模式
概述
工厂模式:实现了创建者与调用者的分离,即将创建对象的具体过程屏蔽隔离 起来,达到提高灵活性的目的。
其实设计模式和面向对象设计原则都是为了使得开发项目更加容易扩展和维 护,解决方式就是一个“分工”。
工厂模式的分类:
简单工厂模式:用来生产同一等级结构中的任意产品。(对于增加新的产品, 需要修改已有代码)
工厂方法模式:用来生产同一等级结构中的固定产品。(支持增加任意产品)
抽象工厂模式:用来生产不同产品族的全部产品。(对于增加新的产品,无 能为力;支持增加产品族)
核心本质:
实例化对象,用工厂方法代替 new 操作。
将选择实现类、创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。
无工程模式示例:
package com.xxx.java1;
interface Car {
void run();
}
class Audi implements Car {
public void run() {
System.out.println("奥迪在跑");
}
}
class BYD implements Car {
public void run() {
System.out.println("比亚迪在跑");
}
}
public class Client01 {
public static void main(String[] args) {
Car a = new Audi();
Car b = new BYD();
a.run();
b.run();
}
}
简单工厂模式示例:
package com.xxx.jd1;
interface Car {
void run();
}
class Audi implements Car {
public void run() {
System.out.println("奥迪在跑");
}
}
class BYD implements Car {
public void run() {
System.out.println("比亚迪在跑");
}
}
//工厂类
class CarFactory {
//方式一
public static Car getCar(String type) {
if ("奥迪".equals(type)) {
return new Audi();
} else if ("比亚迪".equals(type)) {
return new BYD();
} else {
return null;
}
}
//方式二
// public static Car getAudi() {
// return new Audi();
// }
//
// public static Car getByd() {
// return new BYD();
// }
}
public class Client02 {
public static void main(String[] args) {
Car a = CarFactory.getCar("奥迪");
a.run();
Car b = CarFactory.getCar("比亚迪");
b.run();
}
}
小结:
简单工厂模式也叫静态工厂模式,就是工厂类一般是使用静态方法,通过接收的 参数的不同来返回不同的实例对象。
缺点:对于增加新产品,不修改代码的话,是无法扩展的。违反了开闭原则(对扩展开放;对修改封闭)。
工厂方法模式示例
为了避免简单工厂模式的缺点,不完全满足 OCP(对扩展开放,对修改关闭)。 工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式只有一个(对于一个项目或者一个独立的模块而言)工厂类,而工厂方法模式有一组实现了相同接口的工厂类。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。
package com.xxx.gc;
interface Car {
void run();
}
//两个实现类
class Audi implements Car {
public void run() {
System.out.println("奥迪在跑");
}
}
class BYD implements Car {
public void run() {
System.out.println("比亚迪在跑");
}
}
//工厂接口
interface Factory {
Car getCar();
}
//两个工厂类
class AudiFactory implements Factory {
public Audi getCar() {
return new Audi();
}
}
class BydFactory implements Factory {
public BYD getCar() {
return new BYD();
}
}
public class Client {
public static void main(String[] args) {
Car a = new AudiFactory().getCar();
Car b = new BydFactory().getCar();
a.run();
b.run();
}
}
总结:
简单工厂模式与工厂方法模式真正的避免了代码的改动了?没有。在简单工厂模式中,新产品的加入要修改工厂角色中的判断语句;而在工厂方法模式中,要么将判断逻辑留在抽象工厂角色中,要么在客户程序中将具体工厂角色写死(就像 上面的例子一样)。而且产品对象创建条件的改变必然会引起工厂角色的修改。 面对这种情况,Java 的反射机制与配置文件的巧妙结合突破了限制——这在 Spring 中完美的体现了出来。
抽象工厂模式
抽象工厂模式和工厂方法模式的区别就在于需要创建对象的复杂程度上。而且抽象工厂模式是三个里面最为抽象、最具一般性的。
抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象
而且使用抽象工厂模式还要满足一下条件:
1) 系统中有多个产品族,而系统一次只可能消费其中一族产品。
2) 同属于同一个产品族的产品以其使用。
接口的相关练习
面试题:排错
interface A {
int x = 0;
}
class B {
int x = 1;
}
class C extends B implements A {
public void pX() {
//System.out.println(x); //x不明确
System.out.println(super.x); //B中的x
System.out.println(A.x); //A中的x
}
public static void main(String[] args) {
new C().pX();
}
}
注释代码中x不明确,编译错误
练习2
定义一个接口用来实现两个对象的比较。
interface CompareObject{
public int compareTo(Object o); //若返回值是 0 , 代表相等; 若为正数,代表当 前对象大;负数代表当前对象小
}
定义一个Circle类,声明redius属性,提供getter和setter方法
定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。在 ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半 径大小。
定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo 方法比较两个类的半径大小。
思 考 : 参 照 上 述 做 法 定 义 矩 形 类 Rectangle 和 ComparableRectangle 类 , 在 ComparableRectangle类中给出compareTo方法的实现,比较两个矩形的面积大小。
package com.xxx.exer3;
public interface CompareObject {
public int compareTo(Object o);
}
package com.xxx.exer3;
public class Circle {
private Double radius;
public Circle() {
super();
}
public Circle(Double radius) {
super();
this.radius = radius;
}
public Double getRadius() {
return radius;
}
public void setRadius(Double radius) {
this.radius = radius;
}
}
package com.xxx.exer3;
public class ComparableCircle extends Circle implements CompareObject{
public ComparableCircle(double radius) {
super(radius);
}
@Override
public int compareTo(Object o) {
if(this == o) {
return 0;
}
if (o instanceof ComparableCircle) {
ComparableCircle c = (ComparableCircle)o;
//错误的,如2.3和2.1强转后为0
// return (int)(this.getRadius() - c.getRadius());
//正确的
// if (this.getRadius() > c.getRadius()) {
// return 1;
// } else if(this.getRadius() < c.getRadius()){
// return -1;
// }else {
// return 0;
// }
//当属性radius声明为包装类时,可以调用包装类的方法
return this.getRadius().compareTo(c.getRadius());
} else {
// return 0;
throw new RuntimeException("传入的数据类型不匹配");
}
}
}
package com.xxx.exer3;
public class InterfaceTest {
public static void main(String[] args) {
ComparableCircle c1 = new ComparableCircle(2.3);
ComparableCircle c2 = new ComparableCircle (2.5);
int compareValue = c1.compareTo(c2);
if(compareValue > 0) {
System.out.println("c1大");
}else if (compareValue < 0) {
System.out.println("c2大");
}else {
System.out.println("一样大");
}
int compareValue1 = c1.compareTo(new String());
System.out.println(compareValue1);
}
}
类的成员之五:内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内 部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。
内部类的分类:局部内部类、成员内部类(静态、非静态)
实例化内部类的对象
//创建静态成员类的实例
Person.Dog dog = new Person.Dog();
dog.show();
//创建非静态成员类的实例
Person p = new Person();
Person.Bird bird = p.new Bird();
作为外部类的成员
调用外部类的结构
可以被static修饰
可以被四种不同的权限修饰
作为一个类
类内可以定义属性、方法、构造器等
可以被final修饰,表示此类不能被继承,即不使用final,可以被继承
可以被abstract修饰为抽象类
调用外部类的结构
public void display(String name) {
System.out.println(name);//形参
System.out.println(this.name);//内部类的name
System.out.println(Person.this.name);//外部类的name
}
package com.xxx.java2;
public class InnerClassTest {
public static void main(String[] args) {
// 创建静态成员类的实例
Person.Dog dog = new Person.Dog();
dog.show();
// 创建非静态成员类的实例
Person p = new Person();
Person.Bird bird = p.new Bird();
bird.sing();
bird.display("niao");
}
}
class Person {
String name = "人类";
int age;
public void eat() {
System.out.println("人:吃饭");
}
// 成员内部类
// 静态成员内部类
static class Dog {
String name;
int age;
public void show() {
System.out.println("卡拉是条狗");
}
}
// 非静态成员内部类
class Bird {
String name = "鸟";
public void sing() {
System.out.println("我是一只小小鸟");
eat();// Person.this.eat();
}
public void display(String name) {
System.out.println(name);//形参
System.out.println(this.name);//内部类的name
System.out.println(Person.this.name);//外部类的name
}
}
public void method() {
// 局部内部类
class AA {
}
}
{
// 局部内部类
class BB {
}
}
public Person() {
// 局部内部类
class CC {
}
}
}
开发中如何使用局部内部类
package com.xxx.java2;
public class InnerClassTest1 {
// 开发中很少见
public void method() {
// 局部内部类
class BB {
}
}
//返回一个实现了Comparable接口的类的对象
public Comparable getComparable() {
//方式一
//创建一个实现了Comparable接口的类:局部内部类
// class MyComparable implements Comparable{
//
// @Override
// public int compareTo(Object o) {
// return 0;
// }
//
// }
// return new MyComparable();
//
//方式二
return new Comparable(){
@Override
public int compareTo(Object o) {
return 0;
}
};
}
}
总结:成员内部类和局部内部类,在编译后,都会生成字节码文件。
格式:
成员内部类:外部类$内部类名.class
局部内部类:外部类$数字 内部类名.class
注意点:
在局部内部类的方法中,如果调用局部内部类所声明的方法中的局部变量的话要求此局部变量声明为final(
JDK7及之前的版本,要求此局部变量显式声明为final
JDK8及之后的版本,可以省略final的声明
)