接口与继承
接口就像是一种约定,实现某个接口,就相当于承诺了某种约定
创建接口关键字:interface
- 声明的方法只是一个空方法,没有方法体
实现接口关键字:implements
- 实现方法体,必须每个方法都实现
一个类中当然可以实现多个接口
在较为复杂的系统中进行大量运用接口,可以条理清晰地把项目组织起来
对象转型
通常引用和对象的类型是一样的,即Bad boy = new Bad();
而类型转换讨论的问题是引用类型和对象类型不一致的情况下的转换问题
类型转换有时候会成功,有时候会失败,判断的方法是把右边的当做左边来用,是否合理
有继承关系的类转换
子类转父类(向上转型)
- 所有的子类转换为父类都是合法的,类似普通变量的类型转换(低转高,自动进行)
父类转子类(向下转型)
- 父类转子类,有的时候行,有的时候不行,所以必须进行强制转换
- 父转子可成功,成功转换后该引用无法再当为父来使用
- 强制转换失败会抛出异常
- 父类转子类,有的时候行,有的时候不行,所以必须进行强制转换
无继承关系的类转换
- 没有继承关系的两个类,互相转换,一定会失败
实现类转换成接口(向上转型)
- 实现类中一定存在接口中声明的方法,因此,转换是一定成功的
接口转换成实现类(向下转型)
- 接口中未定义有实现类中的其他方法,因此转换会失败
注意:类之间的转换如果是赋值形式,且为向下转换(父传子),如果成功的话,会先将引用指向对象,再转换引用类型,此时转换成功的引用指向的应该为子类,且引用类型也为子类的了
instanceof
A instanceof B判断A是否为B类型,或B的子类
重写
子类可以继承父类的对象方法,在继承后,重复提供该方法,就叫做方法的重写,又叫覆盖 override
子类调用重写的方法调用就会执行重写的方法,而不是从父类的方法
多态
- 操作符的多态
+可以作为算数运算,也可以作为字符串连接
- 类的多态
- 父类引用指向子类对象
类的多态
都是同一个类型,调用同一个方法,却能呈现不同的状态
若方法只是有一点区别,只需要定义一个方法,不同的子类重写方法即可适应很多情况,剩下写更多方法的功夫
条件:
父类(接口)引用指向子类对象只要有继承关系(implements 接口)就可以了- 调用的方法有重写
public class Man(){
public saySomething(){
System.out.println("i am a man.");
}
}
public class Goodman extends Man {
public saySomething(){ //条件2 方法的重写
System.out.println("i am a good man.");
}
}
public class Badman extends Man {
public saySomething(){
System.out.println("i am a bad man.");
}
}
public static main(String[] args){
Man pes1 = new Goodman();
Man pes2 = new Badman(); //条件1 父类引用指向子类
per1.saySomething();
per2.saySomething(); //实现多态
}
若父类中实现接口,即使子类没有implements也可以重写接口,实现接口多态
隐藏
与重写类似,方法的重写是子类覆盖父类的对象方法,隐藏是子类覆盖父类的类方法
当实例化对象引用为父类,指向子类时,即使子类隐藏了类方法,该对象调用的类方法还是父类的
super
实例化一个子类时,会先调用其父类的构造方法,再调用其构造方法
子类构造方法会默认调用父类的无参构造方法
若父类有带参构造方法,子类的构造方法中使用
super(xxx)可以调用父的带參构造方法- 同理,使用this()调用子类中的有参构造方法
使用
super.xxx调用父属性使用
super.xxx()调用父方法
父类提供了有参构造方法,系统不会再提供无参构造方法
创建子类对象时,会先调用其父类的构造方法,而此时父类没有无参构造,所以需要用super关键字去调用父类的有参构造方法
且要写在子类构造方法的前面
Object类
- Object类是所有类的父类
- 声明一个类的时候,默认是继承了Object
- Object类提供一个toString方法,所以所有的类都有toString方法
- toString()的意思是返回当前对象的字符串表达
- 当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件当它被垃圾回收的时候,它的finalize() 方法就会被调用。
- equals() 用于判断两个对象的内容是否相同
==用于判断两个引用,是否指向了同一个对象- hashCode方法返回一个对象的哈希值
- 线程同步相关方法
- wait(), notify(), notifyAll()
- getClass()会返回一个对象的类对象,属于高级内容,不适合初学者过早接触,关于类对象的详细内容请参考反射机制
finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用的,可以重写子类的finalize方法
final
final修饰类,方法,基本类型变量,引用的时候分别有不同的意思。
修饰类 表示该类不能被继承,其子类会出现编译错误
修饰方法 该方法无法被重写
修饰基本类型变量 表示该变量只有一次赋值的机会
修饰引用 该引用只有1次指向对象的机会
修饰常量 指的是可以公开,直接访问,不会变化的值
抽象类
在类中声明一个方法,这个方法没有实现体,是一个“空”方法
这样的方法就叫抽象方法,使用修饰符“abstract"
当一个类有抽象方法的时候,该类必须被声明为抽象类,并且其子类必须提供方法实现
抽象类可以没有抽象方法,且抽象类无法被直接实例化,除非在实例化时完成抽象方法
抽象类与接口的区别
- 子类只能继承一个抽象类,不能继承多个,子类可以实现多个接口
- 抽象类可以定义public,protected,package,private,静态和非静态属性,final和非final属性,但是接口中声明的属性,只能是public,静态的,final的,即便没有显式的声明
注:抽象类和接口类都可以有实体方法。接口中的实体方法叫做默认方法
内部类
非静态内部类
静态内部类
匿名类
本地类
- 非静态内部类 只有当外部类的对象存在,才有意义
- 即
new 外部类().new 内部类();或外部对象.new 内部类(); - 非静态内部类,是可以直接访问外部类的private实例属性的
- 即
public class Test {
class Littleclas{
System.out.println("hello");
}
public static void main(String[] args) {
Test one = new Test(); //实例化内部类必须建立在一个外部类对象的基础之上
Littleclas two = one.new Littleclas();
}
}
静态内部类 实例化不需要一个外部类的实例为基础,可以直接实例化
即
new 外部类.静态内部类();因为没有一个外部类的实例,所以在静态内部类里面不可以访问外部类的实例属性和方法
除了可以访问外部类的私有静态成员外,静态内部类和普通类没什么大的区别
public class Test {
static class Littleclas { //不能访问外部类的实例属性和方法,可以访问私有静态的成员
System.out.println("hello");
}
public static void main(String[] args) {
Test.Littleclas = new Test.Littleclas(); //实例化静态内部类
}
}
- 匿名类 指的是在声明一个类的同时实例化它,使代码更加简洁精练
- 有的时候,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。这样的类,叫做匿名类
public abstract class Test {
public abstract void saySomething();
public static void main(String[] args) {
Test one = new Test() {
public void saySomething() {
System.out.println("hello");
}
}
}
}
- 本地类 可以理解为有名字的匿名类
- 内部类与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置
- 本地类和匿名类一样,直接声明在代码块里面,可以是主方法,for循环里等等地方
public abstract class Test {
public abstract void saySomething();
public static void main(String[] args) {
class Onething extends Test { //与匿名类的区别在于,本地类有了自定义的类名
public void saySomething() {
System.out.println("hello");
}
}
Onething one = new Onething();
}
}
注:匿名类中使用外部的局部变量,外部的局部变量必须修饰为final,在jdk8中,已经不需要强制修饰成final了,如果没有写final,不会报错,因为编译器偷偷的帮你加上了看不见的final
默认方法
默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法
public interface What {
public void comeOn();
default public void saySomething() {
System.out.println("what's new?");
}
}
同时继承了多个接口的 同名默认方法,就必须在实现类中重写该方法 从而免去到底调用哪个接口的默认方法这个模棱两可的问题
假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的类,都需要做改动。但是引入了默认方法后,原来的类,不需要做任何改动,并且还能得到这个默认方法通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类