1.接口interface的含义
Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征,但没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
- 数据类型: 引用类型
- 格式: public interface 接口名称{} 接口名称遵从大驼峰 各个英文单词首字母大写
- 备注:一般情况下,.java文件生成.class。虽然接口用interface,源代码仍然是.java,字节码文件仍然是.class
- 演变新增功能的历程:
java7:接口可以包含的内容 常量、抽象方法。
java8:接口可以包含的内容 常量、抽象方法、默认方法、静态方法
java9:接口可以包含的内容 常量、抽象方法、默认方法、静态方法、私有方法
1.1 java接口内容的详细介绍
在IDEA中,新建class中,选择类别kind的interface;在任何版本java中,接口都能定义抽象方法。
- 抽象方法的定义格式
public abstract 返回值类型 方法名称(参数类型、参数名称){
方法体
}
1.2.接口的定义格式
public interface 接口名称{
抽象方法
public abstract 返回值类型 方法名称(参数列表);
}
注意:接口中的抽象方法有以下特殊的地方:
- 接口中的抽象方法,修饰符必须是两个固定的关键字,public abstract(可随意省略)
- 接口中的抽象方法,三要素(返回值类型 方法名称(小驼峰) 参数列表)随便定义
1.3接口使用步骤
1.接口不能直接使用,也就是不能new接口;必须有一个实现类(相当于子类) 来 实现(相当于extends)该 接口
参考:继承中 public class 子类名称 extends 父类名称{ 方法体 }
2.接口中 public class 实现类名称 implements 接口名称{ 覆盖重写接口中全部抽象方法,也就是去掉abstract 并加上方法体 大括号
3.创建实现类的对象,进行使用。
一般而言,接口的实现类,建议名称由接口名称+Impl组成,其中impl为implement(实施)的简写。
1 package cn.itcast.day04.demo02; 2 3 public interface MyInterfaceDemo02 { 4 public abstract void methodAbs(); 5 6 } 7 8
1 package cn.itcast.day04.demo02; 2 3 public class MyInterfaceDemo02Impl implements MyInterfaceDemo02{ 4 5 @Override 6 public void methodAbs() { 7 System.out.println("接口方法已执行"); 8 9 } 10 } 11 12 13 14 15
1 package cn.itcast.day04.demo02; 2 3 public class Demo02Main { 4 public static void main(String[] args) { 5 MyInterfaceDemo02Impl impl = new MyInterfaceDemo02Impl(); 6 impl.methodAbs(); 7 } 8 } 9
java8中,增加默认方法,解决接口升级的问题,它的定义方法:
public default 返回值类型 方法名称 (参数列表){方法体}
切记:默认方法 可以有方法体。
在一般开发环境中,接口的实现类已经完成某一个版本的接口抽象方法的覆盖重写,但是如果新增一个抽象方法,那么是否所有实现类全部增加重写,压力巨大,此时默认方法就起到了作用
- 接口的默认方法的关键点
1.可以通过接口实现类对象,直接调用
2.可以被接口实现类进行覆盖重写。
3.默认方法,会被实现类继承下去,也可以在实现类中进行覆盖重写
1 package cn.itcast.day04.demo02; 2 3 public class MyInterfaceDemo02Impl implements MyInterfaceDemo02{ 4 5 @Override 6 public void methodAbs() { 7 System.out.println("接口抽象方法在实现类中已执行"); 8 9 } 10 @Override 11 public void m(){ 12 System.out.println("覆盖重写了接口的默认方法"); 13 } 14 }
在后面知识中,接口的默认方法可以拼接函数模型。
java8中,增加静态方法,也就是带着static修饰符
格式:public static 返回值类型 方法名称(参数列表){方法体}
注意:接口方法不能通过实现类来调用,在main方法中直接用接口名称.静态方法(参数),直接使用。因为静态和对象没有关系,如果允许对象调用可能会冲突,所以不需要通过对象。
1 package cn.itcast.day04.demo02; 2 3 public class Demo02Main { 4 public static void main(String[] args) { 5 MyInterfaceDemo02Impl impl = new MyInterfaceDemo02Impl(); 6 impl.methodAbs(); 7 impl.m(); 8 MyInterfaceDemo02.m1(); 9 } 10 } 11 12
java9中,增加私有方法,也就是带着static修饰符,私有方法在实现类中将无法调用。分为两种
- 普通私有方法 解决默认方法的重复代码问题
private 返回值类型 方法名称(参数列表){方法体}
- 静态私有方法 解决静态方法的重复代码问题。
private static 返回值类型 方法名称(参数列表){方法体}
1.4.接口中的常量定义和使用
接口中,可以定义一些常量(类似于变量,但常量的不同在于不能变),但需要public,static ,final来修饰;
格式 public static final int 常量名称 = 10;使用了final表明不可变。只要在接口中就是常量,不带 public static final 也是常量,必须赋值,不能不赋值。一般而言 常量名称用大写,如果多单词建议下划线分割如: NUM_OF
1 package cn.itcast.day04.demo02; 2 3 public interface MyInterfaceDemo02 { 4 public static final int XYZ = 20; 5 public default void m(){ 6 System.out.println("接口的默认方法1"); 7 x(); 8 } 9 }
1 package cn.itcast.day04.demo02; 2 3 public class Demo02Main { 4 public static void main(String[] args) { 5 MyInterfaceDemo02Impl impl = new MyInterfaceDemo02Impl(); 6 impl.methodAbs(); 7 impl.m(); 8 MyInterfaceDemo02.m1(); 9 System.out.println(MyInterfaceDemo02.XYZ); 10 } 11 } 12
使用时,直接用接口名.常量名即可
1.5接口的总结“
在Java 9+版本中,接口的内容可以有:
- 1. 成员变量其实是常量,格式:
[public] [static] [final] 数据类型 常量名称 = 数据值;
注意:
常量必须进行赋值,而且一旦赋值不能改变。
常量名称完全大写,用下划线进行分隔。
- 2. 接口中最重要的就是抽象方法,格式:
[public] [abstract] 返回值类型 方法名称(参数列表);
注意:实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类。
- 3. 从Java 8开始,接口里允许定义默认方法,格式:
[public] default 返回值类型 方法名称(参数列表) { 方法体 }
注意:默认方法也可以被覆盖重写
- 4. 从Java 8开始,接口里允许定义静态方法,格式:
[public] static 返回值类型 方法名称(参数列表) { 方法体 }
注意:应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法
- 5. 从Java 9开始,接口里允许定义私有很乏,格式:
普通私有方法:private 返回值类型 方法名称(参数列表) { 方法体 }
静态私有方法:private static 返回值类型 方法名称(参数列表) { 方法体 }
注意:private的方法只有接口自己才能调用,不能被实现类或别人使用。
同时注意:
- 接口不能有静态代码块;不能有构造方法;
原因是如果有构造方法,那么应该就可以new对象使用,实际接口的实现类不可以如此。
- 一个类的直接父类只有一个,但是可以同时实现多个接口。
public class MyInterfaceImpl implements MyInterfaceA,MyInterfaceB{
覆盖重写所有方法
}
1 package cn.itcast.day04.demo02; 2 3 public interface MyInterfaceDemo02 { 4 public abstract void methodA(); 5 6 } 7
1 package cn.itcast.day04.demo02; 2 3 public interface MyInterfaceDemo03 { 4 public abstract void methodB(); 5 6 } 7 8 9
1 package cn.itcast.day04.demo02; 2 3 public class MyInterfaceDemo02Impl implements MyInterfaceDemo02,MyInterfaceDemo03{ 4 5 @Override 6 public void methodA() { 7 System.out.println("覆盖重写了接口A的抽象方法"); 8 } 9 @Override 10 public void methodB() { 11 System.out.println("覆盖重写了接口B的抽象方法"); 12 } 13 } 14 15 16
1 package cn.itcast.day04.demo02; 2 3 public class Demo02Main { 4 public static void main(String[] args) { 5 MyInterfaceDemo02Impl impl = new MyInterfaceDemo02Impl(); 6 impl.methodA(); 7 impl.methodB(); 8 } 9 } 10
- 所有的类都有父类,它相当于在定义处有extends Object
- 如果实现类的多个接口中,存在重复的抽象方法,那么只需要覆盖重写一次即可
- 如果实现类没有覆盖重写所有接口的所有抽象方法,那么实现类必须是抽象类
- 如果实现类实现的所有接口中,存在多个重复的默认方法,那么一定要对冲突的默认方法进行覆盖重写。
- 一个类的直接父类的方法和接口中的默认方法产生了冲突,则优先使用父类方法。public class Zi extends Fu implements MyInterface{ 方法体}
- 类与类之间是单继承的,直接父类只有一个
- 类与接口之间是多实现的,一个类可以实现多个接口;
- 接口与接口之间是多继承的。多个父接口的抽象方法重复,没关系,但多个父接口中的默认方法重复就得覆盖重写,且子接口必须default关键字。
1.6 静态方法定义:
①、格式
在java类中(方法中不能存在静态代码块)使用static关键字和{}声明的代码块:
public
class
CodeBlock {
static
{
System.out.println(
"静态代码块"
);
}
}
②、执行时机
静态代码块在类被加载的时候就运行了,而且只运行一次,并且优先于各种代码块以及构造函数。如果一个类中有多个静态代码块,会按照书写顺序依次执行。后面在比较的时候会通过具体实例来证明。
③、静态代码块的作用
一般情况下,如果有些代码需要在项目启动的时候就执行,这时候就需要静态代码块。比如一个项目启动需要加载的很多配置文件等资源,我们就可以都放入静态代码块中。
④、静态代码块不能存在任何方法体中
这个应该很好理解,首先我们要明确静态代码块是在类加载的时候就要运行了。我们分情况讨论:
对于普通方法,由于普通方法是通过加载类,然后new出实例化对象,通过对象才能运行这个方法,而静态代码块只需要加载类之后就能运行了。
对于静态方法,在类加载的时候,静态方法也已经加载了,但是我们必须要通过类名或者对象名才能访问,也就是说相比于静态代码块,静态代码块是主动运行的,而静态方法是被动运行的。
不管是哪种方法,我们需要明确静态代码块的存在在类加载的时候就自动运行了,而放在不管是普通方法还是静态方法中,都是不能自动运行的。
⑤、静态代码块不能访问普通变量
这个理解思维同上,普通变量只能通过对象来调用,是不能放在静态代码块中的。
2. 面向对象-多态(polymorphism)
格式 父类名称 对象名 = new 子类名称();父类引用 指向 子类对象;
或者 接口名称 对象名 = new 实现类名称();
简单一句话就是 左父右子;右侧的子类对象就被当做父类使用。类似于一只猫当做一个动物来看,实现子类就是父类,从而进一步实现面向对象的多态性,对象的多态性。
2.1. 多态中,成员变量的访问规则
- 通过对象名.成员变量,看等号左边是谁,优先用谁,否则向上找
- 通过成员方法访问成员变量,看方法属于谁,则优先用谁,否则向上找,主要看子类是否覆盖重写了父类。
2.2. 多态中,成员方法的访问规则
看NEW的是谁,则优先用谁,否则向上找。
但成员方法是 编译看左边,运行看右边
而成员变量是 编译看左边,运行看左边
2.3 多态的好处
多态在很多情况下,书写代码美观和统一性。无论等号右边创建的对象或者实现类的是谁,左边的接口或父类都不会变化。更加的灵活
父类名称 对象名 = new 子类名称();
含义: 右侧创建一个子类对象,把它当成父类。向上转型一定是安全的
类似于
double num = 100 正确
切记,一旦子类的对象向上转型成为父类,那么此时这个对象将无法调用子类的方法。
1 package cn.itcast.day04.Demo; 2 3 public class Demo01 { 4 public static void main(String[] args) { 5 Animal animal = new Cat(); 6 animal.eat();// 向上转型。、 7 animal.sleep(); 8 } 9 } 10
1 package cn.itcast.day04.Demo; 2 3 public class Cat extends Animal{ 4 @Override 5 public void eat() { 6 System.out.println("猫吃鱼"); 7 } 8 public void sleep(){ 9 System.out.println("睡觉"); 10 } 11 } 12
1 package cn.itcast.day04.Demo; 2 3 public abstract class Animal { 4 public abstract void eat(); 5 6 } 7
解决方案:
用对象的向下转型,才能访问子类的自有方法。
子类名称 对象名 = (子类名称)父类对象
含义:将父类对象,还原成原来的子类对象。
未向下转型前,只能调用共有方法,也就是父类方法。向下转型后,将可以调用子类特有方法,但是必须是原来的子类方法,否则调用其他子类方法会出错,
向下转型的安全检测关键字 instanceof
instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法为 boolean
result = obj
instanceof
Class;instanceof 运算符只能用作对象的判断。
1 package cn.itcast.day04.Demo; 2 3 public class Demo01 { 4 public static void main(String[] args) { 5 Animal animal = new Dog(); 6 animal.eat();// 向上转型。、 7 8 if( animal instanceof Dog){ 9 Dog dog = (Dog) animal; 10 dog.eat(); 11 }else{ 12 Cat cat = (Cat) animal; 13 cat.eat(); 14 } 15 16 } 17 } 18
1 package cn.itcast.day04.Demo; 2 3 public class Dog extends Animal{ 4 @Override 5 public void eat() { 6 System.out.println("狗吃骨头"); 7 } 8 } 9
1 package cn.itcast.day04.Demo; 2 3 public class Demo01 { 4 public static void main(String[] args) { 5 Animal animal = new Dog(); 6 animal.eat();// 向上转型。、 7 System.out.println("-===="); 8 method(new Dog()); 9 method(animal); 10 } 11 public static void method(Animal animal) { 12 if (animal instanceof Dog) { 13 Dog dog = (Dog) animal; 14 dog.eat(); 15 } else { 16 Cat cat = (Cat) animal; 17 cat.eat(); 18 } 19 } 20 } 21
格式 对象名 instanceof 子类名称
3.final关键字 最终的 不可改变的
常见的四种用法
- 用来修饰一种类 public final class 类名称{} 含义:这个类再也没有子类,也就是说该类的方法将无法覆盖重写;但改类它可以继承,从而覆盖重写父类的成员方法
1 package cn.itcat.day07.demo01.demo01; 2 // finale可以继承,但其他类不能继承final类 3 public final class MyClass extends Demo01Final { 4 public void methodA(){ 5 System.out.println("final类"); 6 } 7 8 } 9
- 用来修饰一个方法,那么这个方法就是最终方法,不可以由子类覆盖重写,并且抽象方法abstract是绝对不能加final的,因为abstract与final冲突。
用来修饰一个局部变量,也就是在方法的参数或者方法大括号内,则用final修饰后,该变量将无法进行更改,第二次即便跟之前一样,赋值也不行。
当然对于引用类型,如对象来说,则不可变的是地址
final Student stu = new Student();
则stu传值 只能一次。
- 用来修饰一个成员变量,由于成员变量具有默认值,所有一旦用final修饰,建议要么立刻赋值,要么构造方法赋值(全部构造各个赋值)且set注释掉(因为set会修改)。
3.2.内部类概念与分类
一个类包含另外一个类,简称内部类,例如人体与心脏的关系,汽车和发动机的关系。
- 成员内部类(类当中,方法外)
修饰符 class 外部类名称{
修饰符 class 内部类名称{
}
}
注意事项:
- 内部类用外部类,无论啥修饰符都行
1 package cn.itcat.day07.demo01.demo01.Demo02; 2 3 public class Body { //外部类 4 public class Heart{ // 成员内部类 5 // 成员内部类的方法 6 public void methodHeart(){ 7 System.out.println("心脏跳动"); 8 // 成员内部类访问外部内的私有化成员变量 9 System.out.println("我叫" + name); 10 } 11 12 } 13 private String name;// 私有化,访问获取需要get set 14 // 外部类的方法 15 public void methodBody(){ 16 System.out.println("外部类的方法"); 17 } 18 19 public String getName() { 20 return name; 21 } 22 23 public void setName(String name) { 24 this.name = name; 25 } 26 } 27
那么如何使用成员内部类呢
分两种情况,
- 间接方式:外部类使用内部类,然后main只是调用外部类的方法,在外部内的方法中创建内部类的对象,然后在外部类方法中内部类的对象点内部类的方法。
1 package cn.itcat.day07.demo01.demo01.Demo02; 2 3 public class Body { //外部类 4 public class Heart{ // 成员内部类 5 // 成员内部类的方法 6 public void methodHeart(){ 7 System.out.println("心脏跳动"); 8 // 成员内部类访问外部内的私有化成员变量 9 System.out.println("我叫" + name); 10 } 11 12 } 13 private String name;// 私有化,访问获取需要get set 14 // 外部类的方法 15 public void methodBody(){ 16 System.out.println("外部类的方法"); 17 // 外部类的成员方法使用内部类成员方法,在外部类方法中,创建对象 18 new Heart().methodHeart();//匿名对象访问(没有对象名) 19 } 20 21 public String getName() { 22 return name; 23 } 24 25 public void setName(String name) { 26 this.name = name; 27 } 28 } 29
1 package cn.itcat.day07.demo01.demo01.Demo02; 2 3 public class Main { 4 public static void main(String[] args) { 5 Body body = new Body(); 6 body.setName("王宝强"); 7 body.methodBody();//通过调用外部类的方法,使用内部类 8 9 } 10 } 11
- 直接方式: 创建内部类对象
通常对象定义是:类名称 对象名 = new 类名称();而对于嵌套内外类,需要
外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
访问外部类成员变量、内部类成员变量和方法的局部变量
- 局部内部类(又包含匿名内部类)类当中,方法中,出了这个方法就不能用
特点:
- 不能使用任何的访问修饰符。
- 生成两个.class文件,一个Outer.class ,另一个Outer$LocalInner.class
- 局部内部类只能访问方法中声明的final类型的变量。
修饰符 class 外部类名称{
修饰符 返回值类型 外部方法名称(参数列表) {
修饰符 class 局部内部类名称{
}
}
}
总结四种修饰符在内部类中的应用:
局部内部类,如果要访问所在方法的局部变量,那么这个局部变量必须是有效final的,也就是注意写上final。原因是
如果接口的实现类(或者是父类的子类)只需要使用一次,那么这种情况下就可以省略该类的定义,改为使用匿名内部类
匿名内部类的格式:
接口名称 对象名 = new 接口名称(){
覆盖重写所有抽象方法
};// 大括号里面是类,但这个类没有名称。
1.匿名内部类,在创建对象的时候,只能使用一次