【概念】
1.普通类
类可以看成是创建Java对象的模板
一个类可以包含以下类型变量:
- 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
- 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
- 类变量:类变量也声明在类中,方法体之外,但必须声明为static类型。
2.抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了 不能实例化对象 之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
举例:
1.抽象类(注意到该Employee类没有什么不同,尽管该类是抽象类,但是它仍然有3个成员变量,7个成员方法和1个构造方法。)
但是在main方法中 当我们要 new 这个Emplyee类的时候,就会报错
错误代码如下:
Employee.java:46: Employee is abstract; cannot be instantiated Employee e = new Employee("George W.", "Houston, TX", 43); ^ 1 error所以也就是抽象类 不能 实例化对象。
2.抽象类的继承
然后我们选择指定一个Salary类来 继承 Employee
(通过super()来调用Employee构造方法初始化的参数值)
并写入Salary类自己独有的方法:
此时再使用main方法来实例化Salary,该对象将从 Employee 类继承7个成员方法,且通过该方法可以设置或获取三个成员变量。
Employee e = new Salary(...),那么父类Employee就可以访问子类Salary的所有成员。
(extends只是继承,相当于重写了父类的某些方法,如果要得到访问的权限,还是要通过 父类 引用对象 = new 子类()的方式)
最后的输出结果如下:
Constructing an Employee Constructing an Employee Call mailCheck using Salary reference -- Within mailCheck of Salary class Mailing check to Mohd Mohtashim with salary 3600.0 Call mailCheck using Employee reference-- Within mailCheck of Salary class Mailing check to John Adams with salary 2400.
抽象方法
如果你想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,那么你可以在父类中声明该方法为抽象方法。
Abstract关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。
抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。
public abstract double computePay();
声明抽象方法会造成以下两个结果:
- 如果一个类包含抽象方法,那么该类必须是抽象类。
- 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
如果Salary类继承了Employee类,那么它必须实现computePay()方法:
抽象类总结规定
1. 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
3. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
4. 构造方法,类方法(用static修饰的方法)不能声明为抽象方法。
5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
3.接口
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口与类相似点:
- 一个接口可以有多个方法。
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在 .class 结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
接口特性
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
抽象类和接口的区别
- 1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
接口很多人都会写,也明白它用在什么地方,会产生哪些效果。特别是很多人一提到接口,就脱口而出规范、设计模式,那么对于接口又了解多少呢?本章试着从java中接口的定义和使用出发,谈谈java中接口具有哪些特性。
一、接口
首先,了解接口interface就必须和类class区分开,为什么这么说呢?因为接口和类是两个概念,但接口又和类具有一定的关系。有人不禁会问,怎么说呢?类class是从java.lang.Object类派生而来,但接口interface并不是从某一个特定接口派生而来,两个interface可能没有任何交集,但两个class一定存在交集。接口不是Object的子类,但接口却隐士申明了Object中的所有可继承方法,类似于Object子类继承了它的所有可继承方法,这里申明的方法都是abstract形式,也即只有申明,没有方法体block。
其次,接口的申明形式如下:
[annotation] [modifier] interface identitor [extends interfacelist] {
[public | static | ] [FieldConstant];
[public | abstract | ] [AbstractMethod];
[public | static | ] [MemberType];
}
- 1
- 2
- 3
- 4
- 5
- 6
其中,”[ ]”表示可有可无,而[ a | b ]表示从a或b中选择一个。annotation表示接口的注解;modifier表示接口的修饰符,正如前面所说top interface只能是public, nested interface可是static,在class中还可以是protected或private;identitor表示该接口的名称,一般取名时前缀为”I” + interfaceName 或者 interfaceName + “able”;interfacelist表示继承父接口列表,如Comparable, Serializable,Iterator等;FieldConstant表示接口中的字段;AbstractMethod表示接口中的abstract方法;MemeberType表示接口中的成员,如class,interface。
二、interface种类
java将接口分为两种:normal interface,annotation type。normal这个单词不用解释,既然有两种,那么他们肯定存在差异,也具有一定的共性。下面分别谈谈这两种interface。
三、normal interface
通用接口又可以分为外层接口top interface、内嵌接口nested interface,也即是作为一个单独的接口,还是被嵌入于类中、接口中的接口。它们的接口体body定义相同,唯一的区别在于修饰符的差异。nested interface的修饰符可以是static、public、protected或private(这两者只能是作为class中的内嵌接口才能修饰,而作为top interface中的内嵌接口进行修饰,会编译报错)。
1、FieldConstant常量字段
在接口中定义的字段都被修饰符public static final默认进行了修饰,虽然可以显示的添加public、static或final,但是建议不要添加,因为这是多余的行为,可以添加只是说明字段的修饰符。此处,还需注意一个问题,那就是常量的初始化问题,因为class中的常量可以在定义的时候进行初始化,也可以在构造函数中进程初始化,那么问题是接口中的常量何时进行初始化?因为不能显示的调用接口的初始化,所以常见在定义的时候就必须初始化,否则编译器会抛出初始化Error。其次,接口中的FieldConstant能否被子类继承?可以,如果class实现了interface,那么该接口的实例引用可以引用该接口中的常量字段,如果subinterface继承了该接口,那么subinterface继承了该接口的所有常量字段。
public class Test implements ITest {
public static void main(String... args) {
ITest test = new Test();
//通过实例引用接口ITest的常量字段
System.out.println(test.strTest); //hello world
//通过接口引用常量字段
System.out.println(ITest.strTest); //hello world
//通过子接口引用常量字段
System.out.println(ISubTest1.strTest); // hello world
}
}
public interface ITest {
//字段常量在定义的时候必须进行初始化
String strTest = "hello world";
}
public interface ISubTest1 extends ITest {}
提问:接口中的常量字段能否hide父接口中相同名称的字段?肯定可以!
2、AbstractMethod抽象方法
接口中定义的方法没有方法体,并且修饰符为public abstract默认进行修饰,也就是可以给接口中的方法添加这两种修饰符,但是建议不添加,完全是画蛇添足,理由和常量字段一样。既然是方法,那么就必然涉及到方法的重写、重载以及实现问题。
public interface ITest {
public abstract testMethod();
//可以不用添加public abstract修饰符,编译器默认添加
test();
}
- 重写override
在class中重写,子类通过重写父类的方法来改变通过该方法名进行引用时,所具有的行为。那么,interface的方法重写是什么样的呢?除了方法体,方法重写的签名和返回值都和class中方法的重写要求相同。
public interface ITest {
//定义一个待重写的方法
Object test() throws IOException, ClassCastException;
String test2();
}
public interface ISubTest extends ITest {
//第一种,返回值类型是重写方法的子类型
String test() throws IOException, ClassCastException;
//第二种,异常子句重写
Object test() throws IOException;
String test() throws CLassCastException;
String test() throws EOFException;
//第三种,正常重写
Object test();
String test(Object obj) throws IOException;
}
public class Test implements ISubTest {
//实现子接口的方法,如果子接口没有重写父接口的方法,则必须实现父接口的方法。
//也即,实现子接口申明的、继承的方法。
//第一种,返回值类型是重写方法的子类型
String test() throws IOException, ClassCastException {
return null;
}
//第二种,异常子句重写
Object test() throws IOException {
return null;
}
String test() throws CLassCastException {
return null;
}
String test() throws EOFException {
return null;
}
//第三种,正常重写
Object test() {
return null;
}
String test(Object obj) throws IOException {
return null;
}
//实现子接口继承的方法
String test2(){
return null;
}
}
- 重载overload
重载相对于重写来说没那么复杂,它所要求的是方法的签名不同,所谓方法签名是指方法的返回值和方法的参数组合,如String test(Object obj)的签名signatrue为(Object)String。但是,这里并不能根据返回值的不同来判定重载,而在JVM的底层中是根据signature来进行实现,只是编译器进行了过滤处理。
public interface ITest {
//原始方法
String test();
//第一种,参数个数不同
String test(String str);
//第二种,参数的类型不同
String test(int a);
//第三种,参数的类型和个数不同
String test(String str1, String str2);
}
- 方法的实现
接口中方法的实现都必须反映到实现类中,同时允许class通过继承类实现接口interface。此外,class实现implements接口,那么该class必须实现接口中的所有abstract方法(后面会说JDK8的新特性)。
public class Test {
//父类中定义了test1, 记得实现时修饰符的全限必须是public
public void test1() {
}
}
class A extends Test implements ITest1, ITest2 {
//实现ITest2接口方法,记得实现时修饰符的全限必须是public
public String test2() {
return null;
}
}
interface ITest1 {
//方法被A的父类Test实现
void test1();
}
interface ITest2 {
//方法被A实现
String test2();
}
提问:接口中字段和方法是否可以具有相同名称?肯定可以!
3、MemberType成员类型
class中可以申明内部类innerClass、内部接口innerInterface和内部枚举innerEnum,接口也同样可以,此时对于innerClass、innerInterface成员类型的默认修饰符为public static,那么在定义时可以显示的指定这两个修饰符,但不建议。但innerEnum是内部枚举类型,此时它的默认修饰符为public static final,也就是说在接口中定义的枚举都是常量枚举。
public interface ITest {
//修饰符默认为public static
class NestedClass {
private int a;
NestedClass (int a) {
this.a = a;
}
}
//默认修饰符为 public static
interface INestedInterface {
}
//默认修饰符为public static final
enum NestedEnum {
RED, YELLOW,GREEN;
}
}
提问:接口中的字段是否可以和成员类型具有相同的名字?可以;接口中的方法能否具有和成员类型相同的名字呢?可以。
4、内嵌接口NestedInterface
接口可以作为top interface的内嵌成员,也可以作为class中的内嵌成员。无论作为class还是interface的内嵌成员,其中接口体的定义内如如前所述,但是作为class的成员时,接口申明的修饰符还可以是protected 、private或者static。
四、接口中的defaut方法
考虑这样一种场景,某个已发布的jar中的某个接口需要集成一个新的功能函数,那么此时需要往接口中添加一个abstract方法,而所有implements这个接口的class都必须实现这个abstract方法,如果采用一个接口适配器来实现程序,也就需要在适配器类中进行实现即可,其他情况是否就很麻烦?JDK8中在接口中引入了default方法,它是一个接口实例方法,可以用来继承。那么default方法和接口的abstract方法有什么相同点和特性?具体体现在重写、重载和实现。
public interface ITest {
//abstract抽象方法
void test();
//default默认方法
default void testDefault() {
System.out.println("hello world");
}
//@complile-error默认方法不能作为abstract方法的具体实现
/*default void test() {
}*/
}
- 重写
方法重写的方式就四种,default方法和普通的abstract方法没有区别,都必须满足下列四种情况之一:1、具有和父接口方法相同的返回值、名称和抛出异常;2、具有和父接口方法相同名称、抛出异常,但返回值类型是子类型;3、具有和父接口方法相同的名称、返回值,但抛出异常兼容(子异常类型);4、具有和父接口方法相同的名称,但返回值和抛出异常兼容。
但default方法不能重写abstract方法,它只能重写父类的default方法,同时default方法不能具有和abstract方法相同的名称;
public interface ITest {
//抽象方法
Object test();
//默认方法
default Object testDefault() {
System.out.println("hello world");
}
}
public interface ISubTest extends ITest {
//@compile-error编译错误
//默认方法不能作为abstract方法的重写方法
/*default Object test() {
System.out.println("test");
}*/
//默认方法可以重写默认方法
default String testDefault() {
System.out.prinln("hello world");
}
}