接口
概念:接口是多个类的公共规范。
格式:public interface 接口名{//接口内容}
注意:接口编译后生产的字节码文件仍是.class
接口中包含的内容(不同版本支持不同内容)
Java 7
- 常量
- 抽象方法
Java 8
- 默认方法
- 静态方法
java 9
- 私有方法
接口的创建(定义抽象方法Java 7+)
定义抽象方法的格式([]中可以省略):[public] [abstract] 返回值类型 抽象方法名(参数列表);
接口的使用
注意:接口不能直接new对象使用!!!必须使用“实现类”来“实现”接口
格式:public class 实现类名 implements 接口名{//…必须覆盖重写接口中所有的抽象方法(除非实现类本身就是抽象类)}
如何重写接口中所有的抽象方法:去掉abstract 加上大括号和方法体(抽象方法正常化)
使用:new一个实现类的对象,进行使用
Tips:
- 实现类可以在方法中使用接口中的常量也可以定义一个新的常量去接受接口中的常量。
- 但不可以直接通过实现类对象.常量名来调用接口中的常量。
接口默认方法的定义(Java 8+)
格式:public default 返回值类型 默认方法名(参数列表){//方法体}
用途:当接口升级时,需要添加新的方法。假如接口添加了一个抽象方法,那么接口的所有实现类,必须每个都覆盖重写一遍该抽象方法。
而此时接口添加一个默认方法,实现类不是一定要覆盖重写默认方法的。(也可以重写)
接口默认方法的使用(Java 8+)
- 通过接口实现类对象,直接调用(实现类中没有该方法,会向上找接口中的)。
- 接口的默认方法,也可以被接口的实现类覆盖重写(直接调用实现类中的覆盖重写方法)。
接口的静态方法(Java 8+)
格式:public static 返回值类型 静态方法名(参数列表){//…方法体}
使用步骤:
- 在接口中定义静态方法
- (可省略)创建接口的实现类
- 通过接口名称,直接调用其静态方法。格式:接口名称.静态方法名(参数);(不能通过实现类对象来调用接口中的静态方法!!!)
接口的私有方法(Java 9+)
用途:解决接口中两个默认方法之间重复代码的问题,抽取出这些重复代码作为一个私有方法,仅供接口中的方法使用,不能被实现类使用。
两种私有方法:
- 普通私有方法:解决多个默认方法之间重复代码问题
格式:private 返回值类型 私有方法名(参数列表){//方法体}
- 静态私有方法:解决多个静态方法之间重复代码问题
格式:private static 返回值类型 私有方法名(参数列表){//方法体}
为什么没有总结抽象方法调用私有方法的情况?
答:因为抽象方法根本没有大括号和方法体,更不可能调用到其他方法。
为什么静态方法无法调用普通私有方法?
答:因为静态方法随着接口先创建,而此时普通私有方法还未被创建,无法调用到。
接口私有方法的好处:防止部分(不完整)代码被实现类直接调用。
接口的常量定义和使用
接口的常量(const) 等于接口的成员变量
格式([]中可省略):[public] [static] [final] 数据类型 常量名称 = 数据值;
前缀代表的含义:
- public:接口的实现类可以使用该常亮
- static:static表示该类的,非static则表示属于对象的,只有在创建对象后才有他,而接口无法创建对象。
- final:保证实现类或者子类无法修改该常量的值
注意:
- public static final可以被省略,省略但功能依旧存在,不写也会默认附赠public static final。
- 接口的常量必须进行赋值。
- 常量名称命名规则:字母全部大写,用下划线分割
接口使用过程中的注意事项及特点
使用接口时,需要注意:
- 接口没有静态代码(例:static {})块或者构造方法。
- 一个类可以实现多个接口
- 实现类必须覆盖重写接口中所有的抽象方法(除非实现类本身就是抽象类)
- 如果实现类所实现的多个接口中,有重复的抽象方法,那么只需要覆盖重写一次即可。
- 如果实现类所实现的多个接口中,有重复的默认方法,那么实现类必须对该冲突的默认方法进行覆盖重写。
- 子类同时继承父类并且实现接口时,直接父类的方法和接口中的默认方法产生了冲突,优先使用父类当中的方法。
接口之间的多继承
- 类与类之间是单继承的。直接父类只有一个。
- 类与接口之间是多实现的。一个类可以实现多个接口(例:public class MyInterfaceImpl Implenents MyInterfaceA, MyInterfaceB{})。
- 接口与接口之间是多继承的(例:public interface MyInterface extends MyInterfaceA, MyInterfaceB{})。
- 同时拥有继承和实现时,继承放在前面。
对比:
接口:可以继承多个父接口。
类:只能继承一个父类。
注意事项:
- 多个父接口当中的抽象方法重复。子接口必须覆盖重写一次(去abstract,加方法体)。
- 多个父接口当中的默认方法重复。子接口必须覆盖重写一次(带上default)。
多态性
前提:extends关系或者Implements关系
概念:一个对象拥有多种形态。(例:小明是一个人也是一个学生)
多态的格式和使用
父类引用指向子类对象。
格式:
父类名称 对象名 = new 子类名称();
或者
接口名称 对象名 = new 实现类名称();
注意:实现类实现的是子接口,那么也可以使用—>父接口名称 对象名 = new 实现类名称();
多态中成员变量的使用特点
访问成员变量的两种方式:
- 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。
- 简介通过成员方法访问成员变量:看方法属于谁,优先用谁,没有则向上找。
多态中成员方法的使用特点
访问成员方法的规则:看等号右边new 的是谁,优先用谁,没有则向上找。
口诀:编译看左边,运行看右边。
对比:
成员变量:编译看左边,运行看左边。
成员方法:编译看左边,运行看右边。
使用多态的好处
减少代码量,方法的参数列表是员工类,那就可以传入员工类的子类了。
对象的上下转型
对象的向上转型
格式:父类名称 对象名 = new 子类名称();
弊端:对象一旦向上转型为父类,则无法调用子类原本特有的内容。
对象的向下转型
格式:子类名称 对象名 = (子类名称)父类对象;
参考:int num = (int)4.0;
用途:当想要调用子类中特有内容时,则需要用到向下转型。
用instanceof关键字进行类型判断
如何得知一个父类引用的对象,本来是什么子类。
格式:
boolean isflag = 对象名 instanceof 类名称;
// 如果希望掉用子类特有方法,需要向下转型
// 判断一下父类引用animal本来是不是Dog
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse();
}
// 判断一下animal本来是不是Cat
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
}
final 关键字
代表最终的、不可改变的。 常见四种用法
- 修饰局部变量
- 修饰成员变量
- 修饰类
- 修饰方法
修饰类
格式:public(or [default]) final class 类名称{//..} 含义:当前这个类不能有任何子类。(太监类,反证:如果final类有子类,那子类能重写父类中方法,final就不是不可改变的) 注意:一个类如果是final的,其中所有的成员方法都无法进行覆盖重写(因为没儿子。)
修饰方法
格式:修饰符 final 返回值类型 方法名(参数列表){//方法体} 注意事项:
- 当final修饰方法时,该方法无法被覆盖重写。
- 对于类、方法来说,abstract关键字和final关键字不能同时使用,因为矛盾。
修饰局部变量
注意:final修饰的局部变量:“一旦赋值,终生不变” 格式: final 数据类型 变量名 = 数据值; 或者 final 数据类型 变量名; 变量名 = 数据值; 对于基本数据类型来说,不可变说的是变量当中的数据不可改变。 对于引用数据类型来说,不可变说的是变量当中的地址值不可改变。 —》地址值不变,但调用对象方法修改成员变量可能会指导数据改变
修饰成员变量
- 使用final修饰,成员变量照样不可改变
- 虽然成员变量有默认值,但使用了final,必须手动赋值。(不会再有默认值)
- 对于final修饰的成员变量,要么直接赋值,要么通过构造方法赋值
- 如果通过构造方法赋值,则必须保证类中的所有重载的构造方法,都最终对final的成员变量进行赋值。
四种权限修饰符
Java中有四种权限修饰符: public > protected > (default) > private 同一个类(我自己) YES YES YES YES 同一个包(我邻居) YES YES YES NO 不同包子类(我儿子) YES YES NO NO 不同包非子类(陌生人) YES NO NO NO 注意事项:(default)并不是关键字“default”,而是根本不写。
内部类的概念
如果一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类。 例如:身体和心脏的关系。又如:汽车和发动机的关系。 分类:
- 成员内部类
- 局部内部类(包括匿名内部类)
成员内部类
总结:
- 编译后产生两个字节码(外部类名$内部类名.class)
- 内部类的3种使用方法
- 成员的访问
- 其他类想调用外部类的内部类:外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
格式:修饰符 class 外部类名{ 修饰符 class内部类名{ //.. } //.. } 注意:内用外,随意访问;外用内,需要内部类对象
如何使用成员内部类
- 间接方法:在外部类的方法当中,使用内部类;然后main调用外部类的方法
- 直接方法,公式:外部类名.内部类名 对象名 = new 外部类名().内部类名();
内部类的同名变量访问
内部类想访问外部内的同名变量,格式:外部类名.this.变量名
局部变量内部类
总结:
- 在外部方法内创建内部类对象,通过内部类.内部方法调用内部类中方法。在通过外部类.外部方法调用。
- 不能定义静态成员
- 不能用修饰符
概念:一个类定义在一个方法内部,就是局部类(仅当前方法可使用它) 格式:修饰符 class 外部类名{ 修饰符 返回值类型 方法名(参数列表){ class 内部类名{ //… } //…内部类名 对象名 = new 内部类名(); 对象名.方法();or对象名.内部类成员变量 } //… }
定义类时权限修饰符规则
- 外部类:public/(default)
- 成员内部类:public/(default)/protected/private
- 局部内部类:什么都不能写
局部内部类的final问题
局部内部类,如果希望访问所在方法的局部变量。那么这个局部变量必须是【有效final的】。(仅赋值一次) 备注:从Java 8+开始,只要局部变量实时不变,那么final关键字可以省略。 原因:
- new出来的对象在堆内存中。
- 局部变量跟着方法走,在栈内存中。
- 方法出栈后,局部变量也会消失。
- new出来的对象则会等到垃圾回收时消失,所以对象会先copy一份局部变量的值,此时就必须保证局部变量是不变的。
匿名内部类
总结:
- 匿名内部类编译后名称:外部类名$数字.class
- 没有类名和实例对象名
- 可以继承父类不能用extends,可以实现接口不能用implements
例1(实现总结3):
public interface Transport {void run(); }
public abstract class SubClass {public void method(){}; }
public class Test {public static void main(String[] args) {Transport car = new Transport() {@Overridepublic void run() {System.out.println("匿名内部类重写接口中方法");}};SuperClass zi = new SuperClass(){@Overridepublic void method(){System.out.println("匿名内部类重写父类抽象类的方法");}};} }
car.run(); zi.method();
如果接口的实现类(或者是父类的子类),只需要使用一次。 那么这种情况下,就可以省略该类的定义,而改为使用【匿名内部类】 匿名内部类的定义格式: 接口名称 对象名 = new 接口名称(){ //覆盖重写所有抽象方法 }; 对格式“new 接口名称(){}”进行解析 1.new代表创建对象的动作 2.接口名称就是匿名内部类需要实现哪个接口 3.{…}这才是匿名内部类的内容 另外还要注意几点问题: 1.匿名内部类,在【创建对象】的时候,只能使用唯一一次 如果希望多次创建对象,而且类的内容(重写的方法)一样的话,那么就需要使用单独定义的实现类了。 2.匿名对象,在【调用方法】的时候,只能调用唯一一次 如果希望同一个对象调用多次方法,那么必须给对象起个名字。 3.匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】 强调:匿名内部类和匿名对象不是一回事。
静态内部类
总结:
- 直接创建对象
- 只能访问外部类中的静态成员
内部类的代码实现
使用内部类来实现多继承
public class SuperClass01 { public void method1(){ System.out.println("父类01的方法"); } } public class SuperClass02 { public void method2(){ System.out.println("父类02的方法"); } } public class SubClass { //使用内部类实现多继承 public class InnerClass01 extends SuperClass01{ public void method01(){ //直接调用父类方法 super.method1(); } } public class InnerClass02 extends SuperClass02{ //重写父类方法 public void method2(){ System.out.println("子类的方法2"); } } } public class Test001 { public static void main(String[] args) { SubClass.InnerClass01 one = new SubClass().new InnerClass01(); SubClass.InnerClass02 two = new SubClass().new InnerClass02(); one.method1(); two.method2(); } }
解决同时继承类和实现接口时,有同名方法的问题。
public class SuperClass { public void method(){ System.out.println("父类中的方法"); } } public interface MyInterFace { void method(); } 1.同一个类同时继承类和实现接口(实现类)中重写该同名的冲突方法 public class SubClass extends SuperClass implements MyInterFace{ public void method(){ System.out.println("子类中方法"); } } 2.用外部类继承父类,用内部类实现接口 public class SubClass extends SuperClass{ public void method(){ System.out.println("子类重写方法"); } class InnerSubClass implements MyInterFace{ @Override public void method(){ System.out.println("实现类重写方法"); } } public InnerSubClass getInnerClass(){ return new InnerSubClass(); } } public class Test { public static void main(String[] args) { SubClass one = new SubClass(); one.method();//子类重写方法 SubClass.InnerSubClass two = one.getInnerClass(); two.method();//实现类重写方法 } }
类作为成员变量类型
写一遍代码
接口作为成员变量类型
写一遍代码