# 抽象类
# 应用场景
类继承主要是扩充已有类的的功能,子类可以根据需要选择是否重写父类的中的方法,这样一个父类是无法对子类做出任何强制性的重写约定。为了解决这种设计问题,提出了抽象类,抽象类与普通不一样的地方是增加了抽象方法,且不能被单独实例化,只有被子类继承才能实例化,且子类必须重写所有的抽象方法:
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();
}
}
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);
}
}
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 |
它们的关系如下:
在基础类型和包装类的转换过程中,会涉及到装箱和拆箱的概念:
- 装箱:将基本数据类型保存到包装类中,一般利用包装类的构造方法完成。
- 拆箱:从包装类中将数据保存到基本数据类型。
来看下包装类:
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");
}
}
}
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");
}
}
}
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);
}
}
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);
}
}
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();
}
}
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();
}
}
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();
}
}
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);
}
}
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);
}
}
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;
}
}
2
3
4
5
6
7
8
9
10
11
12