Day15 抽象类

一、抽象类

必须要重写的方法所在的类—>抽象类

1.抽象方法

(1)抽象方法:将共性的行为(方法)抽取到父类之后,由于每一个子类执行的内容不一样,所以,再父类中不能确定具体的方法体,该方法就可以定义为抽象方法。
(2)抽象类:包含抽象方法的类。

2.定义格式

抽象方法:

修饰符 abstract 返回值类型 方法名 (参数列表)public abstract void work();

抽象类

abstract class 类名字 { }

3.细节

(1)抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
(2)抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
(3)抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
(4)抽象类的子类,必须重写抽象父类中所有的抽象方法,否则子类也必须定义成抽象类,编译无法通过而报错。
(5)抽象类存在的意义是为了被子类继承。

二、接口

接口是一种规则,更多的侧重为一种行为

1.定义接口

//接口的定义格式:
interface 接口名称{
    // 抽象方法
}

// 接口的声明:interface
// 接口名称:首字母大写,满足“驼峰模式”

2.接口的使用

类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements关键字。

/**接口的实现:
    在Java中接口是被实现的,实现接口的类称为实现类。
    实现类的格式:*/
class 类名 implements 接口1,接口2,接口3...{
}

实现类还可以在继承一个类的同时实现多个接口

public class 类名 extends 父类 implements 接口名1,接口名2 { }

3.成员特点

(1)成员变量

只能是常量,默认修饰符:public static final

(2)构造方法

没有

(3)成员方法

默认修饰符
JDK7以前:只能定义抽象方法,默认修饰符:public abstract(不写,但java会自动加上)
JDK8新特性:接口中可以定义有方法体的方法
JDK9新特性:可以定义私有方法

4.接口与类的关系

(1)类和类

继承关系,单继承,多层继承

(2)类和接口

1)实现关系,单实现,多实现,继承一个类的同时实现多个接口
2)一个类实现一个接口,要重写所有的方法(因为接口里是抽象方法)
3)一个类实现多个接口,且接口中有重名的方法(重名的方法只需要重写一次即可)

(3)接口和接口

继承关系,单继承,多继承(实现类实现了子接口,要重写所有的方法)

5.扩展

(1)JDK8以后的接口

1)定义有方法体的方法

<1>解决接口升级的问题
<2>默认方法的定义
格式:

public default 返回值类型 方法名(参数列表){}
public default void show(){}

—>默认方法不是抽象方法,所以不被强制重写。但如果被重写,重写时去掉关键字default
—>public可以省略,default不能省略
—>如果事先多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写

2)定义静态方法

<1>需要用static修饰
<2>定义格式

public static 返回值类型 方法名(参数列表){}
public static void show(){}

<3>注意
—>静态方法只能通过接口名调用,不能通过类名或对象名调用
—>public可以省略,static不能省略
—>不能被重写

(2)JDK9新增方法

1)接口中的私有方法

<1>普通私有方法(服务普通的私有方法)
格式:

private 返回值类型 方法名(参数列表){}
private void show(){}

<2>静态私有方法(服务于静态私有方法)
格式:

private static 返回值类型 方法名(参数列表){}
private static void methond(){}

(3)接口的应用

1)接口代表规则,是行为的实现
2)当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式成为接口多态

(4)适配器设计模式

1)解决各种问题的套路
2)如果一个接口中,有10个抽象方法,但是我在实现类中,只需要用其中一个,该怎么办?

可以在接口跟实现类中间,新建一个中间类(适配器类)
让这个适配器类去实现接口,对接口里面的所有的方法做空重写
让子类继承这个适配器类,想要用到哪个方法,就重写哪个方法。
因为中间类没有什么实际的意义,所以一般会把中间类定义为抽象的,不让外界创建对象

三、内部类

—>类的五大成员之一
—>在一个类里再定义一个类
—>应用场景:
一个事物内部还有一个独立的事物,内部的事物脱离外部的事物无法独立使用(内部类单独没有意义)
—>可以直接访问外部类的成员,包括私有;外部类要访问内部类的成员,必须创建对象

1.成员内部类*

写在成员位置,外部类的成员

(1)创建

protected修饰—>其他包的子类,本包的类
1)外部直接创建成员内部类的对象(非私有)

外部类.内部类 变量 = new 外部类().new 内部类();

private修饰成员内部类,其他类就不能直接创建对象,只能在本类中创建对象。但是可以使用下面这种方法创建(在外部类中定义一个方法提供内部类的对象)
2)在外部类中定义一个方法提供内部类的对象(private修饰内部类时)

public class Outer{
	String name;
	private class Inner{}
	public Inner getInstance(){
		return Inner();
	}
}
psvm{
	Outer o = new Outer();
	o.getInstance();
	Outer.Inner inner = o.getInstance();//报错,因为Inner是私有的
	Object inner = o.getInstance();//正确
	
}

(2)内部类获取外部类成员变量

psvm{
	Outet.Inner oi = new Outer().new Inner();
	oi.show();
public class Outer{
	private int a = 10;
	private class Inner{
		private int a = 20;
		public void show(){
			int a = 30;
			sout(??)//10
			sout(??)//20
			sout(??)//30
		}
	}
}
就近原则
sout(a);//30
sout(this.a);//20
sout(Outer.this.a);//10
内存图

1.Outer.class,Outer$Inner.class,Test.class字节码文件加载到方法区
2.栈内存Outer.Inner oi
3.(Outet.Inner oi = new Outer())new关键字—>在堆内存中开辟外部类对象内存空间<001>(int a = 10)
4.new Inner(),在堆内存中开辟内部类的地址空间<002>(int a = 20),与此同时,给内部类添加一个隐藏的内部类成员(Outer this <001>)用来记录外部类变量的地址值
5.此时oi存储的是002
6.show()方法加载到栈内存,int a=30入栈,sout(a)先在本方法里找,打印出30
7.sout(this.a)输出调用者地址值中的a,既内部类对象里<002>的a,即打印20
8.sout(Outer.this.a),输出Outer this<001>(外部类对象的地址值)的a,即10

2.静态内部类*

静态内部类只能访问外部类中的静态变量静态方法,如果想要访问非静态需要创建对象(根据内存理解)
特殊的成员内部类

(1)创建格式

外部类.内部类  变量 = new  外部类.内部类构造器;//实际new的是内部类的对象

(2)调用

* 调用非静态方法的格式:先创建对象,用对象调用
* 调用静态方法的格式:外部类名.内部类名.方法名();

3.局部内部类*

(1)定义在方法中的类。
(2)外界无法直接使用,需要在方法内部创建对象并使用
(3)该类可以直接访问外部类的成员,也可以访问方法内的局部变量
(public、static、final只能修饰成员变量,不能修饰局部变量)

public class Outer{
	int a = 10;
public void show(){

	int b = 20;
	class Inner{//局部内部类
		String name;
		int age;
		public void methond1(){
			sout(局部内部类中的methond1方法);
		}
		public static void methond2(){
			sout(局部内部类中的methond2静态方法);
		}
	}
	//创建局部内部类的对象
	Inner i = new Inner();
	sout(i.name);
	sout(i.age);
	i.methond1();
	Inner.methond2();//关于静态方法的调用
}
}

测试类

public class Test{
	public static void main(String[] args){
		Outer o = new Outer();
		o.show();//nulll,20,10,sout(methond1),sout(methond2)
		
	}
}

4.匿名内部类

是内部类的简化写法。他是一个隐含了名字的内部类。开发中,最常用到的内部类就是匿名内部类了。
匿名内部类是成员内部类的一种。❌
可以写在成员位置,也可以写在局部位置

(1)格式

new 类名或者接口名() {
     重写方法;//重写所有的抽象方法,
};

包含了:

  • 继承或者实现关系
  • 方法重写
  • 创建对象

所以从语法上来讲,这个整体其实是匿名内部类对象

(2)推导

以前:

public class Student implements Swim{//Swim是接口
	@Override
	public void swim(){
		sout(重写了游泳的方法);
	}
}

匿名内部类:

new Swim(){
	@Override
	public void swim(){
		sout(重写了游泳的方法);
	}
};//这个整体是一个对象,大括号和里面的内容才是一个类

1.public class Student implements Swim实际上是类名
2.删去public class Student implements后的内容,这个才是真正的内容
3.删去public class Student implements,变成了一个没有名字的类
4.这个没有名字的类想要实现Swim接口,把Swim写在大括号前面,表示这个没有名字的类实现了Swim接口,所以需要在类中重写接口里的所有抽象方法
5.还想要创建这个没有名字的类的对象,new。既下面的代码,不创建一个类,直接用用匿名内部类实现接口.

new后面如果是接口,则是实现关系;如果是类,则是继承关系
匿名内部类必须继承一个父类或者实现一个父接口

(3)使用

mthond(Animal a)是定义在测试类里的方法,其中Animal是一个类
//使用
methond(
	new Animal(){
		@verride
		public void swim(){
		sout(重写了游泳的方法);
		}
	}//这个整体可以看作是Animal的子类对象
);

(4)扩展

1)本质

new Swim(){
	@Override
	public void swim(){
		sout(重写了游泳的方法);
	}
};//整体可以理解为Swim接口的实现类对象
 Swim s = new Swim(){
	@Override
	public void swim(){
		sout(重写了游泳的方法);
	}
};
s.swim();//编译看左边,运行看右边

2)调用

new Swim(){
	@Override
	public void swim(){
		sout(重写了游泳的方法);
	}
}.swim();

(5)使用场景

1)当方法的参数是接口或者类时
2)以接口为例,可以传递这个接口的实现类对象
3)如果实现类只要使用一次,就可以用匿名内部类简化代码

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Heliotrope&G

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值