1.接口
一个没有属性,方法全是抽象方法的抽象类,就可以改写为接口:interface
interface Person{
void run();
String getName();
}
接口里所有的方法默认都是public abstract
当子类继承时我们要用extends,但当一个子类继承的父类是接口时要用implements
class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(this.name + " run");
}
@Override
public String getName() {
return this.name;
}
}
在java中,一个子类只能继承一个父类,但一个子类可以继承多个抽象类。
class Student implements Person, Hello { // 实现了两个interface
...
}
2.接口继承
一个interface
可以继承自另一个interface
。interface
继承自interface
使用extends
,它相当于扩展了接口的方法。例如:
interface Hello {
void hello();
}
interface Person extends Hello {
void run();
String getName();
}
此时,Person
接口继承自Hello
接口,因此,Person
接口现在实际上有3个抽象方法签名,其中一个来自继承的Hello
接口。
此时当子类继承时可以直接继承Person类,且会同时继承Hello类。
abstract class | interface | |
---|---|---|
继承 | 只能extends一个class | 可以implements多个interface |
字段 | 可以定义实例字段 | 不能定义实例字段 |
抽象方法 | 可以定义抽象方法 | 可以定义抽象方法 |
非抽象方法 | 可以定义非抽象方法 | 可以定义default方法 |
default方法
在接口中,可以定义default
方法。
public class MyClass implements InterfaceA {
public static void main(String[] args){
}
@Override
public void saySomething() {
// TODO Auto-generated method stub
}
}
interface InterfaceA{
void saySomething();
void sayHi();
}
在上面的代码中,如果在InterfaceA中新增一个sayHi()方法的话,MyClass是无法编译的,除非在MyClass里覆写sayHi().
Default方法是非常有用的,通过在接口定义的方法的访问修饰符前加上关键字default,那么实现类就无需提供该方法的实现了。比如:
public class MyClass implements InterfaceA {
public static void main(String[] args){
}
@Override
public void saySomething() {
// TODO Auto-generated method stub
}
}
interface InterfaceA{
public void saySomething();
default public void sayHi(){
System.out.println("Hi");
}
}
但用了dafault方法后便不用在子类中覆写函数了
public class lianxi {
public static void main(String[] args) {
InterfaceA m = new MyClass();
m.sayHi();
}
}
class MyClass implements InterfaceA {
@Override
public void saySomething() {
}
}
interface InterfaceA{
void saySomething();
default void sayHi(){
System.out.println("Hi");
};
}
由于Java类可以实现多个接口,那么就可能存在这样的情况:两个或多个接口都有一个同名的default接口方法,从而造成冲突。因为Java虚拟机在程序运行时,并不清楚你要使用哪一个default方法。这会导致编译错误。
public class MyClass implements InterfaceA, InterfaceB {
public static void main(String[] args){
MyClass mc = new MyClass();
mc.sayHi();
}
@Override
public void saySomething() {
// TODO Auto-generated method stub
}
}
interface InterfaceA{
public void saySomething();
default public void sayHi(){
System.out.println("Hi from InterfaceA");
}
}
interface InterfaceB{
default public void sayHi(){
System.out.println("Hi from InterfaceB");
}
}
这样的话会编译报错。想要解决的话可以在MyClass类里覆写sayHi()。但调用的时候调用的是MyClass里的sayHi()方法。
如果想指定调用哪一个接口的sayHi()方法,我们可以这么做:
import java.security.acl.Permission;
public class lianxi {
public static void main(String[] args) {
InterfaceA m = new MyClass();
m.sayHi();
}
}
class MyClass implements InterfaceA, InterfaceB {
@Override
public void saySomething() {
// TODO Auto-generated method stub
}
@Override
public void sayHi(){
InterfaceA.super.sayHi();
}
}
interface InterfaceA{
public void saySomething();
default public void sayHi(){
System.out.println("Hi from InterfaceA");
}
}
interface InterfaceB{
default public void sayHi(){
System.out.println("Hi from InterfaceB");
}
}
3.静态字段和静态方法
静态字段
在类中用static修饰的字段被称为静态字段。与实例字段不同的是,每个实例字段在实例中都有属于自己的独立的空间,而静态字段只有一个共享空间,所有的实例都会共享该字段。
public class Main {
public static void main(String[] args) {
Person ming = new Person("Xiao Ming", 12);
Person hong = new Person("Xiao Hong", 15);
ming.number = 88;
System.out.println(hong.number);
hong.number = 99;
System.out.println(ming.number);
}
}
class Person {
public String name;
public int age;
public static int number;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
发现上面的输出结果是 88 99。但在System.out.println(hong.number);时并没有输入hong.number的值输出结果,而且ming.number = 88。输出结果应该是 0 88。为什么加上static时结果会不一样呢?
┌──────────────────┐
ming ──>│Person instance │
├──────────────────┤
│name = "Xiao Ming"│
│age = 12 │
│number ───────────┼──┐ ┌─────────────┐
└──────────────────┘ │ │Person class │
│ ├─────────────┤
├───>│number = 99 │
┌──────────────────┐ │ └─────────────┘
hong ──>│Person instance │ │
├──────────────────┤ │
│name = "Xiao Hong"│ │
│age = 15 │ │
│number ───────────┼──┘
└──────────────────┘
那是因为,静态字段并不属于实例,虽然实例可以访问静态字段,但是它们指向的其实都是Person class
的静态字段。
因此,不推荐用实例变量.静态字段
去访问静态字段,因为在Java程序中,实例对象并没有静态字段。在代码中,实例对象能访问静态字段只是因为编译器可以根据实例类型自动转换为类名.静态字段
来访问静态对象。
推荐用类名来访问静态字段。可以把静态字段理解为描述class
本身的字段(非实例字段)。对于上面的代码,更好的写法是:
Person.number = 99;
System.out.println(Person.number);
静态方法
用static
修饰的方法称为静态方法。
同静态字段相同,静态方法尽管可以写在实例中,但实际上可以看成是属于class的方法,并不属于实例,因此,静态方法内部,无法访问this
变量,也无法访问实例字段,它只能访问静态字段。(相当与本类中的方法只能调用本类的字段,只不过静态方法的类是class类)
public class Main {
public static void main(String[] args) {
Person.setNumber(99);
System.out.println(Person.number);
}
}
class Person {
public static int number;
public static void setNumber(int value) {
number = value;
}
}
接口的静态字段
因为interface
是一个纯抽象类,所以它不能定义实例字段。但是,interface
是可以有静态字段的,并且静态字段必须为final
类型:
public interface Person {
public static final int MALE = 1;
public static final int FEMALE = 2;
}