java基础内容整理七
十六.接口的定义与使用
1.接口的基本概念
上次我们介绍了抽象类的概念。也了解了抽象类的优缺点:抽象类约定了子类的实现要求,但是有单继承局限,即一个子类只能继承一个抽象类。为了解决这个问题,接口应运而生,接口是没有单继承局限的。所以,几乎所有可以使用抽象类的地方都可以直接用接口替换。
接口就是抽象方法和全局常量的集合。
在java中,接口使用interface关键字定义。这里插一句题外话,在我们给自己新建的接口命名的时候,最好在最前面使用大写字母“I”来突出显示它是个接口,例如:
public interface IStudy{
//抽象方法和全局常量
}
public interface IStudent{
//抽象方法和全局常量
}
public class StudentImpl implements IStudent,IStudy{
//实现抽象方法
}
//接口实例化
IStudent student = new StudentImpl();
//实例化后,同一个子类实现的两个接口可以强转
IStudy study = (IStudy)student;
这样便于阅读,也是众多程序员约定俗成的命名方式。
子类使用implements关键字来实现接口,一个子类可以实现多个接口,实现多继承的概念,如果子类不是抽象类,必须实现接口的全部抽象方法。
接口实例化:和抽象类类似,也是通过子类来实现父接口。
特殊的,如果一个子类实现了多个接口,那么在实例化之后,这两个接口可以直接强转,例子见上方的代码。
2.接口的使用限制
- 接口只能用public权限,因为如此,所以接口里的方法可以不用写权限关键字,因为不写也是默认public,而不是default权限。但是在子类实例化方法时,由于class不写权限是默认default权限,违背了覆写的权限相关规定,所以子类必须写上public权限。
- 由于接口的定义就是抽象方法和全局常量的集合。所以所有相关的关键字都可以不用写,包括public,abstract,static等。但是在我们实际的使用过程中,一般来说,我们只定义抽象方法,不定义全局常量。
- 子类可以先用extends关键字来继承一个抽象类然后在使用implements关键字来实现多个接口。
- 抽象类也可以作为子类实现多个接口,但是接口不能继承抽象类。抽象类可以作为中间类为一个接口的方法做统一实现,例如:
public interface IStudent{
//学生需要学习和睡觉
void study();
void sleep();
}
public abstract StudentAbs implements IStudent{
//睡觉是可以统一实现的,但是学习得自己学自己的
@Override
public void sleep(){
system.out.println("睡觉");
}
}
public class Student extends StudentAbs Implememts IStudent{
//自己学习
public void study(){
system.out.println("去xx地方学习");
}
}
例子中类Student又实现了一次IStudent是为了起到强调效果,便于阅读,让人们知道Student本意是为了实现IStudent接口,只是多了一个中间统一实现。
5. 一个接口可以使用extends关键字来继承多个父接口(接口是可以多继承的)。
6. 接口可以定义一系列的内部结构:内部普通类,内部抽象类,内部接口。用static关键字定义的内部接口相当于外部接口。
3.使用接口定义标准
接口有三大核心应用环境:
1.定义操作标准
2.表示能力
3.在分布式开发中暴露远程服务方法
这里我们主要说一下第一种也是最常见的使用环境:定义操作标准。
举个例子来说,对于一个电脑,可以外接很多设备,比如鼠标,u盘,键盘等。对于这些设备来说,如果想要能够给这个电脑使用,就必须遵守一个标准:USB接口的标准,所以可以将usb作为一个接口。得到如下例子:
public interface USB{
//usb需要安装然后使用
void setUp();
void work();
}
class Mouse implements USB{
public void setUp(){
//安装鼠标驱动
}
public void work(){
//鼠标工作
}
}
class Keyboard implements USB{
public void setUp(){
//安装键盘驱动
}
public void work(){
//键盘工作
}
}
//对于电脑需要使用这个标准
class Computer{
//通过标准统一安装工作,不需要给每个usb设备独立写一个工作方法
public void plugin(USB usb){
//连接usb设备工作
usb.setUp();
usb.work();
}
}
这样我们就定义了一个usb标准,所有使用usb接口的设备都要实现这个标准,以便于在电脑上使用。
通过以上例子,我们发现,在接口和多态性结合之后,对于参数的统一更加明确了。接口是在类之上的设计。所以,在编码过程中,我们需要先设计接口,再写实现类。
4.接口的常见设计模式
这里我们泛泛而谈一下接口的两种主要设计模式:工厂模式和代理设计模式,这里只做一点点介绍,具体的详情请等我总结到设计模式在详细说明。
- 工厂设计模式:
对于一个健康的C/S应用来说,我们需要遵守一个基本规则:对程序的修改不应该影响客户端。例如java本身,引入了jvm虚拟机,程序需要在jvm下运行,而jvm可以在任何系统上跑,这样,我们写的程序就不需要为每个系统做对应优化匹配,只需要jvm自己去对应各个系统就好。
而对于我们的程序来说,最大的容易引起客户端改变的东西就是代码的耦合程度,而代码耦合的最大元凶就是对象实例化的new关键字。如果你在客户端new了服务器的对象,那么如果服务器端改变,则客户端必然要改变。
所以针对这种情况,如果我们需要解耦,就可以使用一个工厂类来当中间层。也就是说用工厂类来实例化对象。
客户端只需要传递一个标志,我们的工厂类就可以通过这个标志给他返回同一个接口的不同的子类实例化对象。从而让客户端不需要操心具体的实现,只需要记住几个标志位即可。 - 代理设计模式
所谓的代理设计模式就是每一个接口由两个子类共同实现,其中一个子类负责真实的业务实现。而另一个子类负责作为辅助真实业务的操作。辅助子类就是代理类,代理子类需要将真实实现的子类作为参数,然后在实现接口过程中调用真实业务方法,在前后予以辅助,写一些辅助方法。
代理的本质是所有的真实业务操作都有一个对应的辅助类共同完成。
5.抽象类和接口的区别
区别点 | 抽象类 | 接口 |
---|---|---|
关键字 | abstract | interface |
结构组成 | 抽象方法,普通方法,全局常量,全局变量,属性,构造方法 | 只有全局常量和抽象方法 |
权限 | 各种权限 | 只有public |
子类继承关键字 | extends | implements |
子类限制 | 一个子类只能继承一个抽象类 | 一个子类可以实现多个接口 |
除了单继承,抽象类和接口比较类似。
十七.匿名内部类
如果一个接口的子类只是用一次,那么单独将之作为一个类就显得很浪费,这个时候就可以使用匿名内部类:
public interface IStudent{
void study();
void sleep();
}
IStudent student = new IStudent(){
@Override
public void study() {
// TODO Auto-generated method stub
}
@Override
public void sleep() {
// TODO Auto-generated method stub
}
};
基本上,匿名内部类都只出现在接口或者抽象类的实例化过程中,一般只有抽象类和接口有继承的子类。
匿名内部类在jdk1.8之后有了改进。
十八.Object类
1.Object类简介
Object是java中所有其他类的最终父类,由于对象多态性的向上转型特性,所有的类都可以使用Object接收。所以在开发中,Object类是参数的最高统一类型。
Object类有几个常用方法:
toString():获取对象新型
equals():对象比较
2.取得对象信息
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
Object类的toString()方法可以获取对象信息,但是如果子类不覆写这个方法,该方法返回的是对象的地址信息。如果你使用java的输出方法输出一个对象,那么它输出的就默认为toString方法返回的信息。
一般来说,我们如果想要得到一个对象的信息,都需要覆写toString方法,然后把信息转换成字符串输出,字符串是王道输出方式,因为字符串有一个特性,只要和字符串做相加操作,不管是什么东西,都会自动转换成字符串。
3.对象比较
Object的equals方法就是用于两个对象比较的方法。
public boolean equals(Object obj) {
return (this == obj);
}
在覆写这个方法的时候需要注意一点:因为这个方法是父类拥有的方法,所以在重载时参数不能变,否则就是重载而不是覆写了。
4.接收引用数据类型
Object类可以接收所有的引用数据类型:包括类,接口和数组。
①.数组是可以向上转型为Object类的,同样的Object类也可以向下转型成数组:
int[]b = new int[]{1,2,3};
Object obj = b;
int[] a = (int[]) obj;
②.正常情况下,接口是不能继承任何类的,但是java强制规定了Object类可以接收接口。而且Object类也可以在强转成接口。
Object类真正的达到了所有类的统一。
十九.包装类
1.包装类简介
上一节我们知道,Object类可以接收所有的引用数据类型。但是java的数据类型是分为引用数据类型和基本数据类型的。此时就会有一个问题,基本数据类型该怎么统一参数?这时候,java就增加了一种类,叫做包装类,专门用于处理基础数据类型的问题。
包装类就是把基本数据类型封装在其中的类。
包装类将基本数据类型封装成类这种引用数据类型,以便于使用Object类去接收。
java有八种基本数据类型,相应的,也就有八种包装类。
这八种包装类按照继承关系分为以下两种:
对象性包装类(Object的直接子类,直接继承Object类):Boolean,Character
数值型包装类(Number的直接子类):Byte,Double,Short,Long,Integer,Float
说明:Number类是一个抽象类,其中有六个重要方法:intValue(),shortValue(),byteValue(),longValue(),doubleValue(),floatValue()
这几个方法就是为了从包装类中获取包装的值。
2. 装箱和拆箱
- 装箱:将基础数据类型变成包装类对象的过程。每一个包装类都提供了一个带参数的构造方法,利用这个方法来实现装箱操作
Integer a =new Integer(5);
- 拆箱:将包装类中包装的基本数据类型取出,利用Number类中的xxValue()方法进行。
int b = a.intValue();
- jdk1.5之后,已经可以自动装箱和拆箱了,可以直接用包装类进行加减乘除运算等操作。但是要注意,在比较的过程中,推荐使用equals而不是== 来比较。因为自动装箱和手动装箱同一个基本数据类型,用 == 判断并不相等,可能和你想要的逻辑不一样。
- 用基本数据类型还是包装类?
接收数据使用基本数据类型,保存数据使用包装类,因为基本数据类型没有null这个概念。比如一个int类型,你不给它赋值,他就默认是0。而Integer包装类则是null。
对应简单java类,在我们编写的过程中尽量使用包装类。
3.字符串和基本数据类型相互转换。
在我们实际开发中,由于String的特殊性质,是最常见的数据传输种类。各种数据的输入都是字符串。所有我们自然要考虑一件事:怎么让字符串和基本数据类型能够相互转换。
- 字符串转成基本数据类型:使用包装类的parseXXX()方法,例如
int a = Integer.parseInt("123");
long b = Long.parseLong("123");
//以此类推
注意:如果字符串格式不对,会报错,但是字符串与Boolean数据类型的转换不会报错,如果不匹配,会返回false。
2. 基本数据类型转成String
第一种方法,使用“+”,字符串和任意类型相加会自动将之转成字符串类型。
String a = ""+5;
但是这种方法会产生内存垃圾。
第二种犯法,使用String类的valueOf方法,该方法有多个重载方法,对应各种基础数据类型。
String a= String.valueOf(123);
String b= String.valueOf(123L);
String c= String.valueOf(123D);
//以此类推