接口
1、案例
2、基本介绍
接口:给出一些没有实现的方法,封装到一起,到方法要使用的时候,再具体实现
(需实现所有方法)
PS:
jdk7以前,接口里的所有方法都没有方法体
jdk8以后,接口里可以有静态方法(需要用static修饰)、默认方法(需要使用default修饰),接口中可以有方法的具体实现
3、应用场景
- 制定规定、规范、规格
- 开发软件,定义一个接口,然后由程序员实现接口
4、注意事项
-
接口不能被实例化(原因和抽象类一样,因为接口“不完整”)
-
接口中所有的方法是public方法(需要别的类实现它)
-
接口中抽象方法,可以不用abstract修饰
interface IB {
void hi(); //相当于public abstract void hi();
}
-
一个普通类实现接口,就必须将该接口的所有方法都实现
-
抽象类实现接口,可以不用实现接口的所有方法
-
一个类可以同时实现多个接口
interface IB {
void hi();
}
interface IC {
void say();
}
class Pig implements IB,IC {
@Override
public void hi() {
}
@Override
public void say() {
}
}
- 接口中的属性,只能是final,而且是public static final修饰
- static:接口属性是属于接口本身而不是实现接口的类的实例,即接口属性和接口绑定,直接通过接口就可以调用
- final:接口属性是常量,一旦定义后就不能修改其值(但是可以被继承,也就是可以被使用)
interface IB {
int n1 = 10; //等价 public static final int n1 = 10;
}
- 一个接口不能继承其它的类,但是可以继承多个别的接口
interface ID extends IB,IC {}
- 接口的修饰符只能是public和默认,这点和类的修饰符是一样的
5、练习
- 判断语法是否正确,如果正确,输出什么?
public class Test {
public static void main(String[] args) {
B b = new B();
System.out.println(b.a); //子类可以访问父类的非私有属性、方法
System.out.println(B.a); //a是static属性,可以通过类名调用,子类类名可以调用父类的static属性
System.out.println(A.a);
}
}
interface A {
int a = 23;
}
class B implements A {
}
输出:
6、接口实现vs继承
继承:父子类是一种is关系(包含)
实现:’父子类‘是一种学习关系(子类学习父类的方法)
public class Test {
public static void main(String[] args) {
LittleMonkey wukong = new LittleMonkey("wukong");
wukong.climb();
wukong.swim();
}
}
class Monkey {
String name;
public Monkey(String name) {
this.name = name;
}
public void climb() {
System.out.println("猴子会爬树");
}
}
interface Fish {
public void swim();
}
class LittleMonkey extends Monkey implements Fish {
public LittleMonkey(String name) {
super(name);
}
@Override
public void swim(){
System.out.println("猴子学会了游泳");
}
}
输出:
小结:
当子类继承了父类,就自动拥有弗雷德功能
当子类需要扩展功能,可以通过 实现接口 的方式扩展
可以理解 实现接口 是 对java 单继承机制 的一种补充
总结:
7、接口的多态
1) 多态参数
public class Computer {
//UsbInterface形参是接口类型,这里体现了多态参数
//既可以接收手机对象,也可以接收相机对象
public void work(UsbInterface usbInterface) {
//通过接口调用方法
usbInterface.start();
usbInterface.stop();
}
}
public class Test {
public static void main(String[] args) {
//接口多态
//接口类型变量ia 可以指向实现了IA的接口类 的对象实例
IA ia = new Bike();
ia = new Car();
//继承多态
//类变量aa 可以指向继承了AAA的子类 的对象实例
AA aa = new BB();
aa = new CC();
}
}
interface IA {}
class Bike implements IA {};
class Car implements IA {};
class AA{}
class BB extends AA {};
class CC extends AA {};
2) 多态数组
public class Test {
public static void main(String[] args) {
Usb[] usbs = new Usb[2];
usbs[0] = new Phone();
usbs[1] = new Camera();
for(int i = 0 ; i < usbs.length ; i++){
if(usbs[i] instanceof Phone){ //判断运行类型是否为Phone
((Phone)usbs[i]).call();
}
usbs[i].work(); //动态绑定
}
}
}
interface Usb{
void work();
}
class Phone implements Usb {
public void call() {
System.out.println("这是手机");
}
public void work(){
System.out.println("手机工作中");
}
}
class Camera implements Usb {
public void work(){
System.out.println("相机工作中");
}
}
3) 接口存在多态传递
public class Test {
public static void main(String[] args) {
IG ig = new Teacher();
//如果IG继承了IH接口,而Teacher类实现了IG接口
//那么就相当于,Teacher类也实现了IH接口
IH ih = new Teacher();
}
}
interface IH {}
interface IG extends IH {}
class Teacher implements IG {}
8、接口多态练习
interface A {
int x = 0;
}
class B {
int x = 1;
}
class C extends B implements A {
//访问接口的 x 就使用A.x
//访问父类的 x 就使用super.x
public void pX() {
System.out.println(A.x + " " + super.x);
}
public static void main(String[] args) {
new C().pX();
}
}
内部类⭐⭐⭐⭐⭐
1、基本介绍
一个类的内部又完整地嵌套了另一个类结构,被嵌套的类成为内部类
内部类是类的第五大成员
(类的五大成员:属性、方法、构造器、代码块、内部类)
内部类最大的特点就是:可以直接访问私有属性。并且可以体现类与类之间的包含关系
案例
class Outer { //外部类
private int n1 = 100; //属性
public Outer(int n1) { //构造器
this.n1 = n1;
}
public void m1() { //方法
System.out.println("m1()");
}
{ //代码块
System.out.println("代码块");
}
class Inner { //内部类
}
}
2、内部类的分类
3、局部内部类
-
定义在外部类的局部位置,通常在方法中,并且有类名
-
可以直接访问外部类的所有成员,包含私有的
-
不能添加访问修饰符,因为它的得就是一个局部变量。局部变量不能使用修饰符。但是可以用final修饰,因为局部变量也可以用final
-
作用域:仅仅在定义它的方法或代码块中
-
局部内部类—访问—>外部类的成员 【访问方式:直接访问】
-
外部类—访问—>局部内部类的成员 【访问方式:创建对象,再访问(注意:必须在作用域内)
-
外部其他类—不能访问---->局部内部类(局部内部类地位是一个局部变量)
-
如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
public class LocalInnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.m1();
}
}
class Outer02 { //外部类
private int n1 = 100;
private void m2(){
System.out.println("Outer02 m2()");
} //私有方法,仅本类可访问
public void m1() {//方法
//1. 定义在外部类的局部位置,通常在方法中
//3. 不能添加访问修饰符
//4. 作用域:仅仅在定义它的方法或代码块中(Inner02只能在m1中使用)
final class Inner02 { //局部内部类(本质还是一个类)(但其地位为局部变量)
private int n1 = 800;
//2. 可以直接访问外部类的所有成员,包含私有的
public void f1() {
//5. 局部内部类可以直接访问外部类的成员
//8. 如果外部类和局部内部类的成员重名时,默认遵循就近原则
// 如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
// Outer02.this本质是外部类的对象,谁调用了m1,Outer02.this就是哪个对象
System.out.println("n1=" + n1);
System.out.println("外部类的n1=" + Outer02.this.n1);
m2();
}
}
//6. 外部类在方法中,可以创建Inner02对象,然后调用方法
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
总结:
1)定义在局部位置
2)作用域在使用它的方法或代码块中
3)本质还是一个类,地位是局部变量
4、匿名内部类⭐⭐⭐⭐⭐
1)本质
1) 本质是类
2) 内部类
3) 该类没有名字
4) 同时还是一个对象
2)注意事项
-
匿名内部类是定义在外部类的局部位置,比如在方法中,并且没有类名
-
为什么要使用匿名内部类?【需求是Tiger类只是使用一次,以后再也不使用了】
-
内部类的基本语法
class Outer04 { //外部类
private int n1 = 10;
public void method() {
//基于接口的匿名内部类
//1. 需求:想使用接口IA,并创建对象
//2. 传统方法:写一个类实现该接口,并创建这个类的对象
//3. 需求是Tiger类只是使用一次,以后再也不使用了
//4. 可以使用匿名内部类来简化
//5. tiger的编译类型? IA
//6. tiger的运行类型? 匿名内部类
/*
底层:系统会分配类名 :外部类类名$1
class XXXX implements IA {
@Override
public void cry() {
System.out.println("老虎叫唤");
}
}
*/
//7. jdk底层在创建匿名内部类Outer04$1,立即创建了Outer04$1的实例,并且把地址返回给tiger
//8. 匿名内部类(Outer04$1)使用一次就不能再使用
IA tiger = new IA() { //接口不能直接new,是因为它不完整。但我new的同时使它变完整(实现其方法),那就可以了
@Override
public void cry() {
System.out.println("老虎叫唤");
}
};
System.out.println("tiger的运行类型=" + tiger.getClass());
tiger.cry();
//分析
//1. father的编译类型?Father
//2. father的运行类型?Outer04$2
//3. 参数列表jack会传递给构造器
Father father = new Father("jack") {
@Override
public void test() {
System.out.println("父亲来了");
}
};
System.out.println("father的运行类型=" + father.getClass());
father.test();
}
}
interface IA {
public void cry();
}
class Father {
public Father(String name) {
System.out.println("接收到name=" + name);
}
public void test(){
}
}
输出:
3)使用
-
匿名内部类既是一个类的定义,本身也是一个对象。
-
因此从语法上看,它既有定义类的他特征,也有创建对象的特征。
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f1();
}
}
class Outer05 {
private int n1 = 99;
public void f1() {
//创建一个基于类的匿名内部类
Person p = new Person() {
@Override
public void hi() {
System.out.println("匿名内部类重写了hi方法");
}
};
p.hi(); //动态绑定,运行类型是Outer05$1
//也可以直接调用
new Person() {
@Override
public void hi() {
System.out.println("匿名内部类重写了hi方法");
}
}.hi();
}
}
class Person {
public void hi() {
System.out.println("Person hi()");
}
}
-
可以直接访问外部类的所有成员,包含私有的(如果外部类和匿名内部类的成员有重名时,遵循就近原则。如果想访问外部类的成员,可以使用
外部类名.this.成员
去访问) -
不能添加访问修饰符,因为它的地位就是一个局部变量
-
作用域:仅仅再定义它的方法或代码块中
-
匿名内部类—访问—>外部类成员【访问方式:直接访问】
-
外部其他类—不能访问—>匿名内部类
4)匿名内部类的最佳实践
- 当作实参直接传递
public class Test {
public static void main(String[] args) {
//匿名内部类当作实参直接传递,简洁高效
f1(new IM() { //new出来的对象,默认返回值为对象类型(IM)
@Override
public void show() {
System.out.println("这是一幅名画");
}
});
//传统方法
f1(new Picture());
}
//静态方法,形参是接口类型
public static void f1(IM im) {
im.show();//动态绑定,和运行类型绑定
}
}
interface IM {
void show();
}
class Picture implements IM {
@Override
public void show() {
System.out.println("这是一幅名画");
}
}
课堂练习2
public class Test {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
cellPhone.alarmclock(new Bell() {
@Override
public void ring() {
System.out.println("懒猪起床了");
}
});
cellPhone.alarmclock(new Bell() {
@Override
public void ring() {
System.out.println("小伙伴上课了");
}
});
}
}
interface Bell {
void ring();
}
class CellPhone {
public void alarmclock(Bell bell){
bell.ring();
}
}
5、成员内部类
-
成员内部类是定义在外部类的成员位置,并且没有static修饰
-
可以直接访问外部类的所有成员,包含私有的
-
可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
-
作用域:和外部类的其他成员一样,作用域为整个类体
-
成员内部类—访问—>外部类成员【访问方式:直接访问】
-
外部类—访问—>成员内部类【访问方式:创建对象再访问】
-
外部其他类—访问—>成员内部类【两种方式】
- 方式一:把成员内部类当成外部类的一个成员,用 外部类对象实例.new 内部类 来调用
- 方式二:在外部类中,编写一个方法,可以返回Inner08对象
Outer08 outer08 = new Outer08();
//外部其他类---访问--->成员内部类【两种方式】
//方式一:把成员内部类当成外部类的一个成员,用 外部类对象实例.new 内部类 来调用
Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say();
//方式二:在外部类中,编写一个方法,可以返回Inner08对象
Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
inner08Instance.say();
- 如果外部类和匿名内部类的成员有重名时,遵循就近原则。如果想访问外部类的成员,可以使用
外部类名.this.成员
去访问
public class Test {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.t1();
//外部其他类---访问--->成员内部类【三种方式】
//方式一:把成员内部类当成外部类的一个成员,用 外部类对象实例.new 内部类 来调用
Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say();
//方式二:在外部类中,编写一个方法,可以返回Inner08对象
Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
inner08Instance.say();
}
}
class Outer08 {
private int n1 = 10;
public String name = "张三";
public class Inner08 { //成员内部类:定义在外部类的成员位置上
double d1 = 0.99;
public void say() {
//可以直接访问外部类的所有成员,包含私有的
System.out.println("Outer08 的 n1 = " + n1);
System.out.println("Outer08 的 name = " + name);
}
}
public void t1() {
//外部类---访问--->成员内部类【访问方式:创建对象再访问】
Inner08 inner08 = new Inner08();
inner08.say();
System.out.println("Inner08 的 d1 = " + inner08.d1);
}
//方式二:在外部类中,编写一个方法,可以返回Inner08对象
public Inner08 getInner08Instance() {
return new Inner08();
}
}
6、静态内部类
-
静态内部类是定义在外部类的成员位置,并且有static修饰
-
可以直接访问外部类的所有成员,包含私有的,但不能直接访问非静态成员
-
可以添加任意访问修饰符
-
作用域:同其他的成员,为整个类体
-
静态内部类—访问—>外部类(比如静态属性)【访问方式:直接访问所有静态成员】
-
外部类—访问—>静态内部类【访问方式:创建对象再访问】
-
外部其他类—访问—静态内部类【两种方式】
-
如果外部类和匿名内部类的成员有重名时,遵循就近原则。如果想访问外部类的成员,可以使用
外部类名.成员
去访问(这里不加this,因为访问的成员是静态的,静态的不能和this一起出现)
public class Test {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.m1();
//外部其他类---访问---静态内部类【两种方式】
//方式一:
new Outer10.Inner10().say();
//等价于:
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
//这里不会创建一个Outer10实例
//静态成员,不用进行类加载(不用加载Outer10)
//但是say()不是静态成员,所以必须创建Inner10类实例对象,进行访问
//方式二:
//↓适用于public static void say() {}
//Outer10.Inner10.say();
//方式三:编写一个方法,可以返回静态内部类的对象实例
Outer10.Inner10 inner101 = outer10.getInner10Instance();
//↓适用于public static Inner10 getInner10Instance(){}
//Outer10.Inner10 inner102 = Outer10.getInner10Instance();
}
}
class Outer10 {
private int n1 = 10;
private static String name = "张三";
static class Inner10 { //静态内部类
private static String name = "张四";
public void say() {
//System.out.println(n1); //错误,不能访问非静态成员
System.out.println(name); //张四
System.out.println(Outer10.name); //访问外部类的同名成员:张三
}
}
public void m1() { //外部类---访问--->静态内部类【访问方式:创建对象再访问】
Inner10 inner10 = new Inner10();
inner10.say();
}
public Inner10 getInner10Instance(){
return new Inner10();
}
}
小结:
(1)内部类有四种:局部内部类、匿名内部类、成员内部类、静态内部类
(2)重点:匿名内部类
new 类/接口(参数列表){
...
};
(3)成员内部类,静态内部类是放在外部类的成员位置,本质就是一个成员