# 抽象类

# 应用场景

类继承主要是扩充已有类的的功能,子类可以根据需要选择是否重写父类的中的方法,这样一个父类是无法对子类做出任何强制性的重写约定。为了解决这种设计问题,提出了抽象类,抽象类与普通不一样的地方是增加了抽象方法,且不能被单独实例化,只有被子类继承才能实例化,且子类必须重写所有的抽象方法:

public abstract class Animal {
    public abstract void Run();
    public abstract void Sleep();
    public abstract void Eat();
}

class Tiger extends Animal{

    @Override
    public void Run() {
        // TODO Auto-generated method stub
        System.out.println("Tiger is running!");
    }

    @Override
    public void Sleep() {
        // TODO Auto-generated method stub
        System.out.println("Tiger is sleeping!");
    }

    @Override
    public void Eat() {
        // TODO Auto-generated method stub
        System.out.println("Tiger is eatting!");
    }
}


public class Main {
    public static void main(String[] args) {
        Tiger t1 = new Tiger();
        t1.Eat();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

注意:

  • 抽象类和抽象方法都需要用abstract进行定义。抽象类定义时不能使用final关键字。
  • 抽象类一定要被子类继承才能实例化,其子类一定要重写抽象类中的所有抽象方法。
  • 抽象类中可以定义普通属性和方法。允许没有抽象类和抽象方法。可以提供static方法。

抽象类的这种特性特别适合模板设计模式:

public abstract class Animal {
    public static final int RUN=1;
    public static final int SLEEP=2;
    public static final int EAT=4;

    public void Behave(int code) {
        switch(code){
            case RUN:{
                this.Run();
                break;
            }
            case SLEEP:{
                this.Sleep();
                break;
            }
            case EAT:{
                this.Eat();
                break;
            }
            case RUN+SLEEP+EAT:{
                this.Run();
                this.Sleep();
                this.Eat();
                break;
            }
        }
    }

    public abstract void Run();
    public abstract void Sleep();
    public abstract void Eat();
}


class Tiger extends Animal{

    @Override
    public void Run() {
        // TODO Auto-generated method stub
        System.out.println("Tiger is running!");

    }

    @Override
    public void Sleep() {
        // TODO Auto-generated method stub
        System.out.println("Tiger is sleeping!");

    }

    @Override
    public void Eat() {
        // TODO Auto-generated method stub
        System.out.println("Tiger is eatting!");
    }

}

class Monkey extends Animal{

    @Override
    public void Run() {
        // TODO Auto-generated method stub
        System.out.println("Monkey is running!");
    }

    @Override
    public void Sleep() {
        // TODO Auto-generated method stub
        System.out.println("Monkey is sleeping!");
    }

    @Override
    public void Eat() {
        // TODO Auto-generated method stub
        System.out.println("Monkey is eatting!");
    }

}


public class Main {
    public static void main(String[] args) {
        Tiger t1 = new Tiger();
        t1.Eat();
        t1.Behave(Animal.EAT);
        Monkey m1 = new Monkey();
        m1.Eat();
        m1.Behave(Animal.RUN);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

# 包装类

Java是面向对象的语言,所有的对象是围绕对象这一核心概念展开,但是基本类型与这一概念没有对应上,需要用类的结构来对基本数据类型(byte,short,int,long,float,double,char,boolean)进行包装。对基本类型进行包装处理后可以像对象一样进行引用传递,同时使用Object类来进行接收,Java为此设置了8个包装类。

基础类型 包装类型
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Character

它们的关系如下:

packagingClass

在基础类型和包装类的转换过程中,会涉及到装箱和拆箱的概念:

  • 装箱:将基本数据类型保存到包装类中,一般利用包装类的构造方法完成。
  • 拆箱:从包装类中将数据保存到基本数据类型。

来看下包装类:

public class Main {
    public static void main(String[] args) {
        Integer intObj = new Integer(100);
        int intNum = intObj.intValue();
        System.out.println("int number:"+intNum);

        Double doubleObj = new Double(10.1);
        double doubleNum = doubleObj.doubleValue();
        System.out.println("double number:"+doubleNum);

        Boolean booleanObj = new Boolean(true);
        boolean booleanValue = booleanObj.booleanValue();
        System.out.println("boolean value:"+booleanValue);

        // 自动拆箱装箱
        Integer intObj2 = 102;
        int intNum2 = intObj2;
        System.out.println("int number2:"+intNum2);

        // 自动拆装箱和String类两种实例化类似,直接赋值的方式会放入常量池中。
        Integer intObj3 = 102;
        if(intObj == intObj2) {
            System.out.println("intObj == intOjb2");
        }
        if(intObj2 == intObj3) {
            System.out.println("intObj2 == intOjb3");
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

从JDK1.5之后Java提供了自动装箱和拆箱机制,但是并没有废除手动装箱,从JDK1.9开始在包装类的构造方法打上了过期的声明。

从上面看出自动拆装箱跟前面的String类的两种实例化很类似,直接赋值的方式会把它放入到常量池中。注意如果使用Integer自动装箱,赋值内容在-128到127之间,可自动引用常量池中的值,否则只能依靠equals比较,为了稳妥起见,统一使用equals来比较。


public class Main {
    public static void main(String[] args) {
        Integer intNum1 = 10;
        Integer intNum2 = 10;
        if(intNum1==intNum2) {
            System.out.println("intNum1 == intNum2");
        }

        Integer intNum3 = 128;
        Integer intNum4 = 128;
        if(intNum3==intNum4) {
            System.out.println("intNum3 == intNum4");
        }

        if(intNum3.equals(intNum4)) {
            System.out.println("intNum3 equals intNum4");
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 类型转换

通过这些包装类可进行类型转换,由字符串转换成其他基本类型:

public class Main {
    public static void main(String[] args) {
        String intString = "123";
        String intString2 = "123a";
        int num1 = Integer.parseInt(intString);
        //java.lang.NumberFormatException 异常
        //int num2 = Integer.parseInt(intString2);
        //如果不是true,会统一转换成false.
        String booleanStr = "hello";
        boolean booleanVal = Boolean.parseBoolean(booleanStr);
        System.out.println(booleanVal);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

由其他类型转换为字符串:

public class Main {
    public static void main(String[] args) {
        int num=10;
        String numString = num+"";
        String numString2 = String.valueOf(num);
    }

}
1
2
3
4
5
6
7
8

# 接口

接口主要用于定义开发标准,拿电脑的USB举例,不管是鼠标,键盘,U盘硬盘,只要实现USB标准协议这个接口,就能在电脑上使用。接口一般使用interface关键字定义,在接口中定义全局常量,public的抽象方法,default方法以及static方法。

interface AnimalCommonBehaviour {
    public static final int RUN = 1;
    public static final int EAT = 2;
    public static final int SLEEP = 4;
    int behaviour = 3;
    // 等价于下面
    // public static final int behaviour=0;

    public abstract void Run();

    public default void Eat() {
        System.out.println("default eatting!");
    }
    public static void Sleep() {
        System.out.println("default sleeping!");
    }

}

interface TigerBehaviour {
    public abstract void Chase();
}

class Animal implements AnimalCommonBehaviour {

    @Override
    public void Run() {
        // TODO Auto-generated method stub
        System.out.println("Animal is running");
    }

}

public class Tiger extends Animal implements AnimalCommonBehaviour, TigerBehaviour {

    @Override
    public void Run() {
        // TODO Auto-generated method stub
        System.out.println("tiger is running");
    }

    @Override
    public void Chase() {
        // TODO Auto-generated method stub
        System.out.println("Tiger is chasing");
    }

}

public class Main {
    public static void main(String[] args) {
        Animal t1 = new Tiger();
        t1.Run();
        System.out.println(Tiger.behaviour);
        t1.Eat();
        AnimalCommonBehaviour.Sleep();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

注意:

  • 子类可实现多个接口,一定要重写所有接口的抽象方法。
  • 如果又要继承,又要实现接口,需要先继承再实现。
  • 接口中定义的变量是不能被改变的,且可通过类名访问,所以上面的behaviour两种声明作用实际上是一致的。
  • 在JDK1.8后,可在接口中使用default定义普通方法,使用static定义静态方法。在default之前一般会在接口和实现类之间加一个抽象类,用来添加方法,有了default关键字之后就不用担心这个了。

# 接口和类

从上面可以看出接口和抽象类很像,它们有啥区别呢?

  • 抽象类由常量,变量,抽象方法,普通方法,构造方法组成,接口由全局变量,抽象方法,普通方法,静态方法组成。
  • 一个类只能继承一个抽象类,但是却能实现多个抽象方法。
  • 接口只能继承接口,而抽象类可以实现接口,也可以继承抽象类。
  • 接口是在类之上的标准,更泛型,比如动物可以是一个公共标准,哺乳动物和卵生动物可以使子标准,而人,狗,猫这些相对具体但是又不表示具体事物的东西可以用抽象类来实现。

# 适用设计模式

接口很适合工厂设计模式以及代理设计模式。

# 工厂设计模式

工厂设计模式会根据一个输入来判断去执行不同的操作,这个模式里会有一个工厂类,会有很多被生成类,根据一个输入来执行不同的操作。

  • 优势:是为了解耦对象的创建和使用,当需要一个子类时不再修改源代码,把对象的创建和使用过程分开。
  • 劣势:需要增加生成类时,会修改工厂类,当生成类很多时,会造成工厂类庞大。
public interface IBallGame {
    public void CanPlay();
    public default String GetBallName() {
        return getClass().getName();
    }
}

class Football implements IBallGame{

    @Override
    public void CanPlay() {
        // TODO Auto-generated method stub
        System.out.println("we can play football!");
    }

}

class Basketball implements IBallGame{

    @Override
    public void CanPlay() {
        // TODO Auto-generated method stub
        System.out.println("we play basketball!");

    }

}

class PingPang implements IBallGame{

    @Override
    public void CanPlay() {
        // TODO Auto-generated method stub
        System.out.println("we play pingpang!");
    }

}

enum IBallGameEnum{
    FOOTBALL(0,"足球"),
    BASKKETBALL(1,"篮球"),
    PingPang(2,"乒乓");

    private int value;
    private String desc;

    public int getValue() {
        return value;
    }

    public String getDesc() {
        return desc;
    }

    IBallGameEnum(int num, String desc) {
        // TODO Auto-generated constructor stub
        this.value=num;
        this.desc=desc;
    }
}

class Factory{
    public static IBallGame getBallInstance(IBallGameEnum ballEnum) {

        if(ballEnum==IBallGameEnum.BASKKETBALL) {
            return new Basketball();
        }

        if(ballEnum==IBallGameEnum.PingPang) {
            return new PingPang();
        }
        return new Football();
    }
}



public class Main {
    public static void main(String[] args) {
        IBallGame pingpangBall = Factory.getBallInstance(IBallGameEnum.PingPang);
        pingpangBall.CanPlay();
        IBallGame footBall = Factory.getBallInstance(IBallGameEnum.FOOTBALL);
        footBall.CanPlay();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

# 代理设计模式

代理设计模式是不关心谁来执行具体操作,我只需知道有一个对象会来实现操作即可:

public interface IOutputDevice {
    public void OutputInfo();
}

class phone implements IOutputDevice{

    @Override
    public void OutputInfo() {
        // TODO Auto-generated method stub
        System.out.println("phone output info!");

    }

}

class printer implements IOutputDevice{

    @Override
    public void OutputInfo() {
        // TODO Auto-generated method stub
        System.out.println("printer output info!");

    }

}

class screen implements IOutputDevice{

    @Override
    public void OutputInfo() {
        // TODO Auto-generated method stub
        System.out.println("screen output info!");
    }

}

class DealInfo{
    private IOutputDevice devices;

    public void setDevices(IOutputDevice devices) {
        this.devices = devices;
    }

    public void DealInfoStep() {
        System.out.println("input info!");
        devices.OutputInfo();
        System.out.println("put data into database!");
    }

}

public class Main {
    public static void main(String[] args) {
        DealInfo dev1 = new DealInfo();
        dev1.setDevices(new printer());
        dev1.DealInfoStep();
        dev1.setDevices(new screen());
        dev1.DealInfoStep();
        dev1.setDevices(new phone());
        dev1.DealInfoStep();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

# 泛型

在Java语言中,为了方便接收统一的类型,提供了一个Object,但是它所代表的范围过大,在使用过程中会出现无法确定数据类型,导致编程的混乱。在JDK1.5之后,引入了泛型概念,可以方便地避免对象强制转型带来的安全隐患问题。它的核心思想在于:在类的属性或方法参数中添加动态标记,在对象实例化的时候动态配置要使用的数据类型。

注意泛型只允许设置引用类型,如果设置基本类型,必须采用包装类的形式。

public class PrintSomething<T> {

    private T contenT;
    public String nameString;

    public void setNameString(String name) {
        nameString=name;
    }

    public T getContenT() {
        return contenT;
    }

    public void setContenT(T contenT) {
        this.contenT = contenT;
    }

    public static void GetInfo(PrintSomething<?> ps) {
        System.out.println(ps.getContenT());
        ps.nameString="xie";
    }

    public static void GetInfo2(PrintSomething ps) {
        ps.setContenT(12);
        System.out.println(ps.getContenT());

    }

    public static void GetNumber(PrintSomething<? extends Number> temp) {
        System.out.println(temp.getContenT());
    }

    public static void GetString(PrintSomething<? super String> temp) {
        System.out.println(temp.getContenT());

    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //JDK 1.5声明操作
        PrintSomething<Integer> p1 = new PrintSomething<Integer>();
        p1.setContenT(123);
        GetInfo2(p1);
        GetInfo(p1);
        GetNumber(p1);
        System.out.println("nameString:"+p1.nameString);

        //JDK 1.7之后简化的声明操作
        PrintSomething<Float> p2 = new PrintSomething<>(); 
        p2.setContenT(14.3f);
        GetInfo(p2);

        PrintSomething<String> p3 = new PrintSomething<String>();
        p3.setContenT("hello world!");
        GetString(p3);
        GetInfo2(p3);

    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

注意:

  • 泛型只允许设置引用数据类型,如果要使用基本数据类型,需要使用包装类。
  • JDK1.7之后简化了定义泛型操作,new后面的可以不用写类型。
  • 如果在实例化变量时不指定类型,所有数据都是通过Object接收。
  • <?>可匹配任意泛型,它比不指定类型好的一点是只能读泛型属性,而不指定类型则不限制,可以读写。
  • <? extends className>:设置泛型的上限,只有当前类及其子类能被使用。<? super className>:设置泛型的下限,只有当前类及其父类能使用。

# 泛型接口

interface MyPrintInfo<T>{
    public void printInfo(T msg);
}

class MyPhone<T> implements MyPrintInfo<T>{

    @Override
    public void printInfo(T msg) {
        // TODO Auto-generated method stub
        System.out.println(msg);
    }

}

class MyPrinter implements MyPrintInfo<Number>{

    @Override
    public void printInfo(Number msg) {
        // TODO Auto-generated method stub
        System.out.println(msg);
    }

}

public class Main {
    public static void main(String[] args) {
        MyPhone<String> MyPhoneObj = new MyPhone();
        MyPhoneObj.printInfo("hello");

        MyPrinter myPrinterObj = new MyPrinter();
        myPrinterObj.printInfo(100);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 泛型方法

public class Main {
    public static void main(String[] args) {
        Integer[] result= PrintSomething(1,2,3,4,5);
        for (int i:result) {
            System.out.print(i+",");
        }
    }

    public static <T> T[] PrintSomething(T... args) {
        return args;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12