第一行代码Java理解
1、抽象类
例如:所有的 Servlet 一定要继承 HttpServlet 类,而 HttpServlet 类会根据用户发出的不同请求(每种请求都通过一个常量表示)调用不 同的方式进行处理。例如:发出的是 get 请求,就调用 doGet()方法,发出的是 post 请求,就调用 doPost()方法。
1.1、抽象类与普通类的对比
-
抽象类继承子类里面会有明确的方法覆写要求,而普通类没有;
-
抽象类只比普通类多了一些抽象方法的定义,其他的组成部分与普通类完全一样;
-
普通类对象可以直接实例化,但是抽象类的对象必须经过向上转型后才可以得到 实例化对象。
1.2、抽象类的使用限制
- 抽象类里面由于会存在一些属性,那么在抽象类中一定会存在构造方法,目的是为属性初始化,并且子类对象实例化时依然满足先执行父类构造再调用子类构造的情况。
- 抽象类不能使用 final 定义,因为抽象类必须有子类,而 final 定义的类不能有子类;
- 抽象类中可以没有任何抽象方法,但是只要是抽象类,就不能直接使用关键字 new 实例化对象。
1.3、模板参考
abstract class Action { // 定义一个抽象的行为类,行为不是具体的
// 定义常量时必须保证两个内容相加的结果不是其他行为,例如:EAT + SLEEP 的结果为 6,不会和其他值冲突
public static final int EAT = 1; // 定义吃的命令
public static final int SLEEP = 5; // 定义睡的命令
public static final int WORK = 7; // 定义工作的命令
/**
* 控制操作的行为,所有的行为都通过类中的常量描述,可以使用 EAT、SLEEP、WORK
* 或者进行命令的叠加使用,例如:边吃边工作,使用 EAT + WORK 来描述
*
* @param flag 操作的行为标记
*/
public void command(int flag) {
switch (flag) { // switch 只支持数值判断,而 if 支持条件判断
case EAT: // 当前为吃的操作
this.eat(); // 调用子类中具体的“吃”方法
break;
case SLEEP: // 当前为睡的操作
this.sleep(); // 调用子类中具体的“睡”方法
break;
case WORK: // 当前为工作的操作
this.work(); // 调用子类中具体的“工作”方法
break;
case EAT + WORK: // 行为组合,本处只是举例说明,不演示
this.eat(); // 调用“吃”的方法
this.work(); // 调用“工作” 的方法
break;
}
}
public abstract void eat(); // 定义子类的操作标准
public abstract void sleep(); // 定义子类的操作标准
public abstract void work(); // 定义子类的操作标准
}
class Robot extends Action {
@Override
public void eat() { // 覆写行为的操作
System.out.println("机器人补充能量!");
}
@Override // 此操作不需要但必须覆写,所以方法体为空
public void sleep() {
}
@Override
public void work() { // 覆写行为的操作
System.out.println("机器人正在努力工作!");
}
}
public class MyTest {
public static void main(String[] args) {
fun(new Robot()); // 传递机器人行为子类
}
/**
* 执行具体的操作行为,假设本处只执行 EAT、SLEEP、WORK3 个行为
* @param act 具体的行为对象
*/
public static void fun(Action act) {
act.command(Action.EAT); // 调用“吃”操作
act.command(Action.SLEEP); // 调用“睡”操作
act.command(Action.WORK); // 调用“工作”操作
}
}
2、接口
抽象类的子类存在一个很大的问题 —— 单继承局限,所以为了打破这个局限,就需要用 Java 接口来解决
1、接口的应用——工厂设计模式(Factory)
客户端不再需要关注具体子类,也不需要关注 Factory 类是怎样处理的。只需要关注如何取得接口对象并且操作。这样的设计在开发中就称为工厂设计模式
解决关键字 new 所带来的耦合度问题 ,让客户端只看见接口而不让其看见子类,
通过一个中间的工具类来取得接口对象,让客户端不再需要关心接口子类,
只要通过 Factory(工厂类)程序类就可以取得接口对象。
**例如:**Spring 框架,其核心目的就是解决这种代码耦合问题。
/*----------------定义接口及其子类---------------------*/
public interface Fruit { //定义接口
void eat();//定义抽象方法
}
class Apple implements Fruit { // 定义接口子类
@Override
public void eat() { // 覆写抽象方法
System.out.println("eat apple");
}
}
class Banana implements Fruit {
@Override
public void eat() {
System.out.println("eat banana");
}
}
/*----------------工厂类---------------------*/
public class Factory {
/**
*取得指定类型的接口对象
* @param className 要取得的类实例化对象标记
* @return 如果指定标记存在,则 Fruit 接口的实例化对象,否则返回 null
*/
public static Fruit getInstance(String className) {
if ("apple".equals(className)) { // 是否是苹果类
return new Apple();
} else if ("banana".equals(className)) { // 是否是香蕉类
return new Banana();
} else {
return null;
}
}
}
/*----------------测试使用工厂类---------------------*/
public class TestDemo {
public static void main(String[] args) {
Fruit fruit = Factory.getInstance("banana");
fruit.eat();
}
}
/*----------------测试不用工厂类---------------------*/
public class TestDemo {
public static void main(String[] args) {
Fruit fruit = new Banana();
fruit.eat();
}
}
对比:
不用工厂类时,执行不同子类的方法需要修改代码(new Apple(),new Banana()),动一发牵全身,耦合
使用工厂类时,通过传入标记可以获取不同子类实例化对象,进而调用其方法
在客户端的操作上取消关键字 new 的使用,而使用 Factory.getInstance()方法根据指定子类的标记取得接口实例化对象,这时客户端不再需要关注具体子类,也不需要关注 Factory 类是怎样处理的,只需要关注如何取得接口对象并且操作。这样的设计在开发中就称为工厂设计模式
2、接口的应用——代理设计模式(Proxy)
代理设计也是在 Java 开发中使用较多的一种设计模式,
所谓代理设计就是指一个代理主题来操作真实主题,真实主题执行具体的业务操作,而代理主题负责其他相关
业务的处理。
就好比在生活中经常使用到的代理上网,客户通过网络代理连接网络,由代理服务器完成用户权限、访问限制等与上网操作相关的操作。
interface Network{ // 定义 Network 接口
public void browse() ; // 定义浏览的抽象方法
}
class Real implements Network{ // 真实的上网操作
public void browse(){ // 覆写抽象方法
System.out.println("上网浏览信息") ;
}
}
class Proxy implements Network{ // 代理上网
private Network network ;
public Proxy(Network network){ // 设置代理的真实操作
this.network = network ; // 设置代理的子类
}
public void check(){ // 与具体上网相关的操作
System.out.println("检查用户是否合法");
}
public void browse(){
this.check() ; // 可以同时调用多个与具体业务相关的操作
this.network.browse() ; // 调用真实上网操作
}
}
public class TestDemo {
public static void main(String args[]){
Network net = null ; // 定义接口对象
net = new Proxy(new Real()) ; // 实例化代理,同时传入代理的真实操作
net.browse() ; // 客户只关心上网浏览一个功能
}
}
程序运行结果:
检查用户是否合法(代理主题)
上网浏览信息(真实主题)