个人博客:www.letus179.com
概念
抽象类:包含抽象方法的类就称为 抽象类;
接口: 抽象方法的集合,方法没有具体实现即不包含方法体。
两者异同
相同点
- 都
不能
被实例化; 接口的实现类
或抽象类的子类
都必须实现接口
或抽象类中
的方法后才能被实例化
不同点
接口
中的方法是高度抽象的,只有定义,没有具体的实现;抽象类
中可以有定义也可以有实现;接口
中的方法默认为public abstract
, 可以直接写public
, 可以都省略不写;接口
中方法修饰符不能是private
, 接口需要实现,所以私有的方法没有意义;抽象类
中的具体实现的方法和普通的类一样,而只有定义的方法必须用abstract
修饰,不然编译不通过
;而且抽象方法
的修饰符不能是private
,static
,synchronized
,native
,能用默认的
,public
和protected
。接口
中定义变量默认为public static final
, 并且要赋予初始值
,不然编译不通过;抽象类
和普通的类一样,可以有自己的成员变量,可以重新赋值;接口
需要实现(implements)
,抽象类
需要继承(extends)
,一个类可以实现多个接口,但是只能继承一个抽象类(单继承多实现)。
接口三问
接口方法能否用private
修饰
不能。因为接口是对外开放的,需要具体的类来实现其中的方法,私有的方法作用范围为本类,在接口中定义私有方法没有任何意义,编译不会通过。
接口方法能否用synchronized
修饰
不能。
synchronized
是一种同步锁
,在修饰方法时,需要具体逻辑,而接口只是定义,因为直接修饰接口中的方法没意义,可以用来修饰接口方法的具体实现
。
接口方法能否用static
修饰
这和jdk版本有关。jdk8之前:不能;jdk8新特性: 能。
——参见JDK8新特性之接口的静态方法和默认方法
举例
这里提供两种方式:jad反编译
和反射
来获取成员变量和方法的相关内容。
jad反编译
下面先给出对应源码,再用jad
反编译.class
文件。反编译执行命令如下:
jad -a -o -s java 类名.class
jad
详细命令参见反编译小工具:jad常用命令介绍
接口.java源码
MyInterface.java
接口源码如下:
public interface MyInterface {
String name = "MyInterface";
void add();
}
接口.class反编译
MyInterface.class
反编译结果如下:
public interface MyInterface {
public abstract void add();
public static final String name = "MyInterface";
}
可以看到,
1.在成员变量(常量)中默认的修饰符是public static final
;
2.在方法中,默认的修饰符是public abstract
抽象类.java源码
MyAbstract
抽象类源码如下:
public abstract class MyAbstract {
public String name;
void get() {
System.out.println("MyAbstract get() ");
}
// 抽象类中的没有实现的方法必须是“abstract”
abstract void delete();
}
抽象类.class反编译
public abstract class MyAbstract {
public MyAbstract() {
}
void get() {
System.out.println("MyAbstract get() ");
}
abstract void delete();
public String name;
}
可以看到,成员方法和成员变量和普通类一样,是什么修饰就是什么。
反射获取
这里新建一个MyTest
类,继承
抽象类MyAbstract
,并且实现
接口MyInterface
反射代码
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class MyTest extends MyAbstract implements MyInterface {
public static void main(String[] args) {
Field[] abstractFields = MyAbstract.class.getDeclaredFields();
Method[] abstractMethods = MyAbstract.class.getDeclaredMethods();
System.out.println("抽象类MyAbstract成员变量反射信息:");
for (Field f : abstractFields) {
System.out.println("变量名:" + f.getName());
System.out.println("修饰符:" + Modifier.toString(f.getModifiers()));
System.out.println("变量类型:" + f.getType());
}
System.out.println();
System.out.println("抽象类MyAbstract成员方法反射信息:");
for (Method m : abstractMethods) {
System.out.println("方法名:" + m.getName());
System.out.println("修饰符:" + Modifier.toString(m.getModifiers()));
System.out.println("返回类型:" + m.getReturnType() + "\n");
}
Field[] interfaceFields = MyInterface.class.getDeclaredFields();
Method[] interfaceMethods = MyInterface.class.getDeclaredMethods();
System.out.println();
System.out.println("接口MyInterface成员变量反射信息:");
for (Field f : interfaceFields) {
System.out.println("变量名:" + f.getName());
System.out.println("修饰符:" + Modifier.toString(f.getModifiers()));
System.out.println("变量类型:" + f.getType());
}
System.out.println();
System.out.println("接口MyInterface成员方法反射信息:");
for (Method m : interfaceMethods) {
System.out.println("方法名:" + m.getName());
System.out.println("修饰符:" + Modifier.toString(m.getModifiers()));
System.out.println("返回类型:" + m.getReturnType());
}
}
@Override
public void add() {
}
@Override
void delete() {
}
}
运行结果
抽象类MyAbstract成员变量反射信息:
变量名:name
修饰符:public
变量类型:class java.lang.String
抽象类MyAbstract成员方法反射信息:
方法名:get
修饰符:
返回类型:void
方法名:delete
修饰符:abstract
返回类型:void
接口MyInterface成员变量反射信息:
变量名:name
修饰符:public static final
变量类型:class java.lang.String
接口MyInterface成员方法反射信息:
方法名:add
修饰符:public abstract
返回类型:void
可以看到,和jad
反编译的结果是一样的。
具体的反射机制
请参见另一篇博文反射机制基础解析