目录
证明观点:什么是类的模板,类的信息是如何在内存空间中进行存储的呢?
2.4 抽象类化有构造函数,抽象类存在的构造函数的意义是什么?
2.7 抽象类A 继承 抽象类B B的抽象方法,A一定覆盖吗?
一、继承
1. 简介
我每天面对是计算机,从学习java语言开始,开始都jdk开始,配置JDK环境变量,然后Hellworld。
然后进行一系列语法的数据,数据类型,循环,判断,运算,类,继承,接口,集合,Map,
io。。。。。。。。。
所有的语言的开发流程,运行流程以及执行流程其实都一样,如下:
总的来说,如下:
- 不同计算语言,虽然有不同语法结构或者语法糖,但是他们开发流程,以及运行流程其实一样的。
- 计算机语言,它是一种人机交互和沟通的一种语言,既然是语言就必须有对应的语法和数据结构,语态等等。
- 就好比,计算语言它是人和机器沟通桥梁。我们底层计算机存储的0|1。计算机语言其实就把现实的一些逻辑转换成计算机可以识别的0|1代码。一句话:java、go 语言等等都在应用程序中的代码转换成0|1和计算机沟通协作。
我们知道,电脑中记事本。记事本比较简单可以记录一句话也可以记录一段文字。但是如果我们想做一
个资料库。
比如这个资料库去保存我们学习的课程视频、课程笔记、课程的相关文件等。
我们会把每一个课程的相关信息的情况放入到这个资料库中。
然后把所以的课程资料通过资料库罗列出来。下面如何通过电脑表现出来呢?又如何通过程序表现出来
呢?
2. 表现形式
2.1. 电脑的表现形式
2.2. 程序的表现形式
对于资料库中的课程来说,程序的表现应该又如下一些东西
接下来我们花一点点时间,把这两个类,资料库和课程做出来,如下:
资料库
DataBase.java
public class DataBase {
}
课程
public class Course{
// 课程标题
private String title;
// 课程描述
private String description;
// 课程大小
private String coursesize;
// 课程时长
private String coursetime;
// 课程价格
private Double price;
// 课程上传时间
private Date createTime;
// 课程目录
private Integer folderId;
public Course() {
}
public void print(){
System.out.println(this);
}
public Course(String title, String description, String coursesize, String coursetime, Double price, Date createTime, Integer folderId) {
this.title = title;
this.description = description;
this.coursesize = coursesize;
this.coursetime = coursetime;
this.price = price;
this.createTime = createTime;
this.folderId = folderId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getCoursesize() {
return coursesize;
}
public void setCoursesize(String coursesize) {
this.coursesize = coursesize;
}
public String getCoursetime() {
return coursetime;
}
public void setCoursetime(String coursetime) {
this.coursetime = coursetime;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Integer getFolderId() {
return folderId;
}
public void setFolderId(Integer folderId) {
this.folderId = folderId;
}
@Override
public String toString() {
return "Course{" +
"title='" + title + '\'' +
", description='" + description + '\'' +
", coursesize='" + coursesize + '\'' +
", coursetime='" + coursetime + '\'' +
", price=" + price +
", createTime=" + createTime +
", folderId=" + folderId +
'}';
}
}
课程放入到资料库
如何把课程放入到资料库中呢?如下:
/**
* 资料库类
*/
public class Database {
// 课程资料容器
private ArrayList<Course> courseTable = new ArrayList<>();
// 1: 添加资料库
public void add(Course course) {
courseTable.add(course);
}
// 2:罗列资料库
public void list() {
for (Course course : courseTable) {
course.print();
}
}
// 运行的目的:就是程序加载的jvm中,未来的启动tomcat
public static void main(String[] args) {
// 1: 创建一个资料库
Database database = new Database();
// 2: 创建文件
Course course1 = new Course("01、课程标准PPT", "这是一个课件", new Date(), "970kb", 0.1d, 1);
Course course2 = new Course("newsprint.css", "这是一个样式文件", new Date(), "12kb", 0.01d, 1);
// 3:把course1 和course2添加到资料库中
database.add(course1);
database.add(course2);
//4: 把资料库的文件罗列处理
database.list();
}
}
打印结果如下:
视频资源
如果要进行扩展需要增加:视频资源
public class Video{
// 视频标题
private String title;
// 视频描述
private String description;
// 视频大小
private String coursesize;
// 视频时长
private String coursetime;
// 视频价格
private Double price;
// 视频播放地址
private String playlink;
// 视频上传时间
private Date createTime;
public Video() {
}
public Video(String title, String description, String coursesize, String coursetime, Double price, String playlink, Date createTime) {
this.title = title;
this.description = description;
this.coursesize = coursesize;
this.coursetime = coursetime;
this.price = price;
this.playlink = playlink;
this.createTime = createTime;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getCoursesize() {
return coursesize;
}
public void setCoursesize(String coursesize) {
this.coursesize = coursesize;
}
public String getCoursetime() {
return coursetime;
}
public void setCoursetime(String coursetime) {
this.coursetime = coursetime;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public String getPlaylink() {
return playlink;
}
public void setPlaylink(String playlink) {
this.playlink = playlink;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "Video{" +
"title='" + title + '\'' +
", description='" + description + '\'' +
", coursesize='" + coursesize + '\'' +
", coursetime='" + coursetime + '\'' +
", price=" + price +
", playlink='" + playlink + '\'' +
", createTime=" + createTime +
'}';
}
public void print(){
System.out.println(this);
}
}
资源库
public class DataBase {
// 1:课程资料容器
private List<Course> courseTable = new ArrayList<Course>();
// 2:视频资源容器
private List<Video> videoTable = new ArrayList<Video>();
// 2: 添加课程到课程资料容器
public void add(Course course) {
courseTable.add(course);
}
// 3: 添加视频资源
public void add(Video video) {
videoTable.add(video);
}
// 4: 罗列出所有的资源库的课程信息
public void listcourse() {
courseTable.forEach(res->res.print());
}
// 5: 罗列出所有的资源库的视频信息
public void listvideo() {
videoTable.forEach(res->res.print());
}
// 5: 找个应用程序运行程序
public static void main(String[] args) {
// 1: 创建资料库
DataBase dataBase = new DataBase();
// 2: 创建课程
Course course1 = new Course();
course1.setTitle("学相伴旅游项目实战课程");
course1.setCoursesize("12M");
course1.setCoursetime("300小时");
course1.setCreateTime(new Date());
course1.setFolderId(1);
course1.setPrice(1499d);
course1.setDescription("学相伴旅游项目实战课程java基础");
Course course2 = new Course();
course2.setTitle("学相伴秋招课程");
course2.setCoursesize("122M");
course2.setCoursetime("300小时");
course2.setCreateTime(new Date());
course2.setFolderId(2);
course2.setPrice(1999d);
course2.setDescription("学相伴旅游项目实战课程java基础");
//创建视频资源
Video video1 = new Video();
video1.setTitle("学相伴秋招课程视频");
video1.setCoursesize("122M");
video1.setCoursetime("300小时");
video1.setCreateTime(new Date());
video1.setPlaylink("xxx.mp4");
video1.setPrice(1999d);
video1.setDescription("学相伴旅游项目实战课程java基础");
// 3: 添加课程到资料库
dataBase.add(course1);
dataBase.add(course2);
// 4:添加视频资源到资料库
dataBase.add(video1);
// 4:打印罗列课程
dataBase.listcourse();
// 4:打印罗列视频
dataBase.listvideo();
}
}
存在问题
上面代码存在什么问题?
这个时候会发现,课程和视频资源的差异,课程的视频大部分的属性情况下两者是相同。
如何解决问题
我们是否可以把公共的东西进行抽离呢?答案是可以的,实现步骤如下:
1:先抽离放入公共类
2:然后让两者进行实现extends即可。
3. 继承
通过继承我们可以把父类中公开的和受保护方法和属性进行让子类进行共享使用。
从而达到复用的目的。
定义父类
Item.java
package com.zheng;
public class Item {
public void print(){
}
}
课程类继承
public class Course extends Item{
// 课程标题
private String title;
// 课程描述
private String description;
// 课程大小
private String coursesize;
// 课程时长
private String coursetime;
// 课程价格
private Double price;
// 课程上传时间
private Date createTime;
// 课程目录
private Integer folderId;
public Course() {
}
public void print(){
System.out.println(this);
}
public Course(String title, String description, String coursesize, String coursetime, Double price, Date createTime, Integer folderId) {
this.title = title;
this.description = description;
this.coursesize = coursesize;
this.coursetime = coursetime;
this.price = price;
this.createTime = createTime;
this.folderId = folderId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getCoursesize() {
return coursesize;
}
public void setCoursesize(String coursesize) {
this.coursesize = coursesize;
}
public String getCoursetime() {
return coursetime;
}
public void setCoursetime(String coursetime) {
this.coursetime = coursetime;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Integer getFolderId() {
return folderId;
}
public void setFolderId(Integer folderId) {
this.folderId = folderId;
}
@Override
public String toString() {
return "Course{" +
"title='" + title + '\'' +
", description='" + description + '\'' +
", coursesize='" + coursesize + '\'' +
", coursetime='" + coursetime + '\'' +
", price=" + price +
", createTime=" + createTime +
", folderId=" + folderId +
'}';
}
}
视频类继承item
public class Video extends Item{
// 视频标题
private String title;
// 视频描述
private String description;
// 视频大小
private String coursesize;
// 视频时长
private String coursetime;
// 视频价格
private Double price;
// 视频播放地址
private String playlink;
// 视频上传时间
private Date createTime;
public Video() {
}
public Video(String title, String description, String coursesize, String coursetime, Double price, String playlink, Date createTime) {
this.title = title;
this.description = description;
this.coursesize = coursesize;
this.coursetime = coursetime;
this.price = price;
this.playlink = playlink;
this.createTime = createTime;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getCoursesize() {
return coursesize;
}
public void setCoursesize(String coursesize) {
this.coursesize = coursesize;
}
public String getCoursetime() {
return coursetime;
}
public void setCoursetime(String coursetime) {
this.coursetime = coursetime;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public String getPlaylink() {
return playlink;
}
public void setPlaylink(String playlink) {
this.playlink = playlink;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "Video{" +
"title='" + title + '\'' +
", description='" + description + '\'' +
", coursesize='" + coursesize + '\'' +
", coursetime='" + coursetime + '\'' +
", price=" + price +
", playlink='" + playlink + '\'' +
", createTime=" + createTime +
'}';
}
public void print(){
System.out.println(this);
}
}
资源库类2
public class DataBase2 {
// 1:课程资料容器
private List<Item> itemTable = new ArrayList<Item>();
// 2: 添加课程到课程资料容器
public void add(Item course) {
itemTable.add(course);
}
// 4: 罗列出所有的资源库的课程信息
public void list() {
itemTable.forEach(res -> res.print());
}
// 5: 找个应用程序运行程序
public static void main(String[] args) {
// 1: 创建资料库
DataBase2 dataBase = new DataBase2();
// 2: 创建课程
Course course1 = new Course();
course1.setTitle("学相伴旅游项目实战课程");
course1.setCoursesize("12M");
course1.setCoursetime("300小时");
course1.setCreateTime(new Date());
course1.setFolderId(1);
course1.setPrice(1499d);
course1.setDescription("学相伴旅游项目实战课程java基础");
Course course2 = new Course();
course2.setTitle("学相伴秋招课程");
course2.setCoursesize("122M");
course2.setCoursetime("300小时");
course2.setCreateTime(new Date());
course2.setFolderId(2);
course2.setPrice(1999d);
course2.setDescription("学相伴旅游项目实战课程java基础");
//创建视频资源
Video video1 = new Video();
video1.setTitle("学相伴秋招课程视频");
video1.setCoursesize("122M");
video1.setCoursetime("300小时");
video1.setCreateTime(new Date());
video1.setPlaylink("xxx.mp4");
video1.setPrice(1999d);
video1.setDescription("学相伴旅游项目实战课程java基础");
// 3: 添加课程到资料库
dataBase.add(course1);
dataBase.add(course2);
// 4:添加视频资源到资料库
dataBase.add(video1);
// 4:打印罗列课程
dataBase.list();
}
}
整个的继承关系
4. 通过继承得到了什么?
那就看父类有什么?父类也是一个类:类里面也就只有属性和方法。得到也就是父类所有的属性和方
法,但是如果子类中有和父类相同的方法和签名的时候,就会引发重写(覆盖),如果我们要明确的是
覆盖的方法可以通过 @Override 关键词标识出来。
子类是不是可以获取父类所有的方法和属性呢?当然不能因为牵涉到一个问题就是:访问权限。
子类可以获取父类中的什么样子的属性和方法呢?如下:
- 公开的 public
- 受保护的 proctected
-
- 如果在开发中,一个父类想让子类去获取成员属性和方法的话,尽量定义成protected
- 如果父类的成员属性和方法私有的,就可以通过公开get方法进行获取私有属性,
- 如果要给父类的私有属性要初始化或者赋值。你可以通过super方法进行给私有属性赋值,或者公开的set方法
- 缺省的(default) (同包中 )
public class Item {
public void print(){
}
}
public class Video extends Item{
public void print(){
System.out.println(this);
}
}
public class Course extends Item{
public void print(){
System.out.println(this);
}
}
5. 访问修饰符
子类是不是可以获取父类所有的方法和属性呢?当然不能因为牵涉到一个问题就是:访问权限。
- 子类不能访问父类私有的成员和属性
- 一般父类定义属性的时候,如果要让其子类共享属性,一般设置为proctected,这样更好!
- 一般父类定义属性的时候,如果要让其子类共享属性,如果设置成private,可以通过super方法进行传递给父类。
- 子类覆盖父类的方法的时候访问权限必须大于父类方法本身的访问权限
6. super关键词
子类集成父类的时候,每个子类的构造函数都有一个隐式的super调用,可以暴露出来。
也可以不暴露出来。而这个super方法默认指向父类的构造函数。
- 每个子类继承父类以后,每个子类的都会有一个默认的隐式super()方法
- 如果一个类没有继承父类,它父类就是Object,这个时候super()方法执行的就是Object的构造功函数
- 这个super()方法的作用,其实大部分的作用:就给父类的私有成员变量进行赋值使用。
- 但是如果父类的无参构造函数一旦被定义成有参数的,子类构造函数的super()方法就必须显示的声明。如果不什么编译不通过。
public class Item {
// 视频标题
private String title;
// 视频描述
private String description;
// 视频大小
private String coursesize;
// 视频时长
private String coursetime;
// 视频价格
private Double price;
// 视频上传时间
private Date createTime;
public Item(String title, String description, String coursesize, String coursetime, Double price, Date createTime) {
this.title = title;
this.description = description;
this.coursesize = coursesize;
this.coursetime = coursetime;
this.price = price;
this.createTime = createTime;
}
public void print(){
}
}
视频信息类
package com.zheng;
import java.util.Date;
public class Video extends Item{
// 视频播放地址
private String playlink;
public Video(String title, String description, String coursesize, String coursetime, Double price, String playlink, Date createTime) {
super(title,description,coursesize,coursetime,price,createTime);
this.playlink = playlink;
}
@Override
public void print(){
System.out.println(this);
}
}
课程类
public class Course extends Item{
// 课程目录
private Integer folderId;
@Override
public void print(){
System.out.println(this);
}
public Course(String title, String description, String coursesize, String coursetime, Double price, Date createTime, Integer folderId) {
super(title,description,coursesize,coursetime,price,createTime);
this.folderId = folderId;
}
public Integer getFolderId() {
return folderId;
}
public void setFolderId(Integer folderId) {
this.folderId = folderId;
}
}
资料库
public class DataBase2 {
// 1:课程资料容器
private List<Item> itemTable = new ArrayList<Item>();
// 2: 添加课程到课程资料容器
public void add(Item course) {
itemTable.add(course);
}
// 4: 罗列出所有的资源库的课程信息
public void list() {
itemTable.forEach(res -> res.print());
}
// 5: 找个应用程序运行程序
public static void main(String[] args) {
// 1: 创建资料库
DataBase2 dataBase = new DataBase2();
// 2: 创建课程
Item course1 = new Course("学相伴旅游项目实战课程","学相伴旅游项目实战课程java基础","12M","300小时",1499d,new Date(),1);
Item course2 = new Course("学相伴秋招课程","学相伴秋招课程","12M","300小时",1499d,new Date(),1);
//创建视频资源
Video video1 = new Video("学相伴秋招课程","学相伴秋招课程","12M","300小时",1499d,"MP4",new Date());
// 3: 添加课程到资料库
dataBase.add(course1);
dataBase.add(course2);
// 4:添加视频资源到资料库
dataBase.add(video1);
// 4:打印罗列课程
dataBase.list();
}
}
7. 如果子类和父类有相同的属性怎么办?
相同的属性,相互隔离互不影响
8. 继承的总结和好处
- 父类除了能够很好的把一些公共的方法和属性进行封装,让其子类继承达到复用以外。
- 继承还有一个很重要的意义:就是分担职责
- 继承可以达到:复用、封装、职责分担的作用。
二、多态
1. 什么是多态
从上面可以得出结论:
- Java中的任何一个类都是多态的,它们能保存不止一种类型的对象。
- 它们可以保存的是除了自身对象以外,还可以保存声明类型的子类的对象。
- 当把子类对象赋值给父类对象的引用的时候,就会引发多态。(也称之为:向上造型)。
2. 向上转型注意的事项
子类的对象可以赋值给父类的变量,就会引发向上造型cast
注意:Java中不存在对象对对象分覆盖和赋值操作。
比如:
String str = "abc";
str = "bcd";
这个时候在内存空间中,是两个内存地址,而不是一个,
首先会在内存空间中开辟一个空间,才能空间存储abc,让str指向,然后在开辟一块内存空间地址存储:“bcd” 。
让其str指向新的地址,原来的地址在jvm中也就是:垃圾内存。
父类的引用对象不能直接赋值给子类的变量
Item item = new Item();
Video video = new Video();
item = video; // 这个是正确的
video = item;//编译错误
可以使用强制类型转换(造型)
Item item = new Item();
Video video = new Video();
item = video; // 这个是正确的
Video video2 = (Video)item;//出现ClassCaseException异常
3. 向上转型总结
- 用括号围起来的类型放在值的前面(只限于对象类型)
- 对象本身并诶呦发生任何变化,所以不是“类型转换”
- 运行时如果不合理就会出现类型转换异常:ClassCastException
- 拿一个子类的对象,当做父类的对象来用使用
- 向上造型是默认的,不需要运算符
- 向上造型总是安全的。也就是 父子
4. 函数的绑定
通过上面分析,Item分析有两个子类型,分别是:Course和Video。
当一个父类存在多个子类型的时候,item就是一种多态变量。
for(Item item : itemList){
item.println(); // 而这种多态变量,会根据自身的数据类型,自动去匹配子类的方法
// 也就是说:item.这个动作的时候,java会自动去匹配实际的类型。而这个事情就称之为多态。
}
item而这种多态变量,会根据自身的数据类型,自动去匹配子类的方法 也就是说:
item 这个动作的时候,java会自动去匹配实际的类型。而这个事情就称之为多态。
而 item.方法 这个动态称之为:函数绑定
- 当通过对象变量调用函数的时候,调用哪个函数这个事情叫做绑定、
- 静态绑定:根据变量的声明类型来决定(也就是说你这个变量的申明类型,我就调用这个申明类型的函数。)
- 动态绑定:根据变量的动态类型来决定(也就是说:当实际管理的对象类型是什么,我就用那个类型的函数, 因为编译器在编译的时候,根本不知道你这个具体的类型是什么?在java中选择都是动态绑定来处理函数的绑定。)
- 在成员函数中调用其他成员函数也可以通过this这个对象变量来调用。
比如在item.print这行的时候,其实在编译的时候并不知道具体的类型是什么。所以只能在运行的时候才能确认具体的类型。
对于像java这样的程序语言来说,默认所有的绑定都是:动态绑定。
5. 覆盖override
- 覆盖前提:继承关系
- 子类和父类中存在名字和参数表完全相同的函数,那么就存在覆盖关系
- 通过父类的变量调用存在覆盖关系的函数时,会调用变量当时所管理的对象所属的子类的函数。
三、Java类&抽象类&接口&枚举
1. 类
- 类:它描述一类事物的的行为和状态(特征)的模板。
- 对象:对象是类的一个实例(对象不是找个女朋友),有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
类模板的作用:就为了给的对象的创建提供了一模板
一个类可以包含以下类型变量:
- 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。 ==局部变量的生命周期:跟随方法执行结束而死亡
- 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。==给对象创建提供了依据模板,成员变量存储在方法区的类信息中。它申请空间的时机,创建对象new User() , 对象死亡就消亡了==
- 类变量:类变量也声明在类中,方法体之外,但必须声明为 static 类型。它存储在方法区的静态区中,而且静态成员在编译的时候就存储进去了。
一个类可以拥有多个方法,在上面的例子中:eat()、run()、sleep() 和 name() 都是 Dog 类的方法。
1.1. 对象管辖的范围
- 成员变量
- 成员方法
1.2. 类管辖的范围
- 静态成员
比如:Item.java
public class Item {
// 视频标题
private String title;
// 视频描述
private String description;
// 视频大小
private String coursesize;
// 视频时长
private String coursetime;
// 视频价格
private Double price;
// 视频上传时间
private Date createTime;
public Item(){
}
public Item(String title, String description, String coursesize, String coursetime, Double price, Date createTime) {
this.title = title;
this.description = description;
this.coursesize = coursesize;
this.coursetime = coursetime;
this.price = price;
this.createTime = createTime;
}
public void print(){
}
public static void main(String[] args) {
Item item = new Item();
item.print();
}
}
证明观点:什么是类的模板,类的信息是如何在内存空间中进行存储的呢?
加载程序到JVM中的时机:
- 执行main函数
- 启动tomcat
1.3. 构造方法
每个类都有构造方法。
如果没有显式地为类定义构造方法,Java 编译器将会为该类提供一个默认构造方法。
在创建一个对象的时候,至少要调用一个构造方法。
构造方法的名称必须与类同名,一个类可以有多个构造方法。
public Item(){
}
public Item(String title, String description, String coursesize, String coursetime, Double price, Date createTime) {
this.title = title;
this.description = description;
this.coursesize = coursesize;
this.coursetime = coursetime;
this.price = price;
this.createTime = createTime;
}
1.4. 创建对象
对象是根据类创建的。在Java中,使用关键字 new 来创建一个新的对象。创建对象需要以下三步:
- 声明:声明一个对象,包括对象名称和对象类型。 (1:准备一个class)
- 实例化:使用关键字 new 来创建一个对象。(2:new 去标识出来创建对象)
- 初始化:使用 new 创建对象时,会调用构造方法初始化对象。(3:会调用构造函数去java堆中申请空间)
public static void main(String[] args) {
Item item = new Item();
item.print();
}
2. 抽象类
含有abstract修饰符的class即为抽象类,abstract 类不能创建实例对象。
含有abstract方法的类必须定义为abstract class,abstract class类中的方法不必是抽象的。
abstract class类中定义抽象方法必须在具体(Concrete)子类中实现,所以,不能有抽象构造方法或抽象静态方法。
如果子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract类型。
抽象类:其是比普通类多了一个抽象方法, 抽象方法它是子类的一种约束,告诉子类一定要覆盖和重写
的方法
2.1 为什么会存在抽象类呢?
原因是在实际的业务中,普通的父类很多时候无法起到约束子类的作用,比如:重写的方法。
- 因为能够让子类重写的方法,大部分情况父类是不需要去做事情,也就是必须让子类自身去实现。普通的父类是起不到约束的作用,也就是说,父类中需要覆盖的方法和普通方法这个时候就难以分辨和区分。所以就有了抽象方法abstract。==后续的接口更加能体现的淋漓尽致。==
- 我们知道抽象类。可以定义抽象方法和非抽象方法。==那么父类中的非抽象方法的意义是什么呢?其实还是回归到继承的概念:职责分担,大部分父类的非抽象方法就是去分担子类的业务,这样便于后续的维护和升级,这样就不需要去修改每个子类。只需要修改父类的方法,当然前提是方法的访问权限必须是:public或者protected的,如果是缺省的就必须在同包package中。
- 抽象类,在平时的业务中那些场景下可以用到抽象类呢?
-
- 比如springmvc路由的继承
- hibernate、jdbctempalte中的通用类的增删改查
- 还有就是spring框架中存在大量的抽象类,其意义就是:责任分担。
2.2 抽象类可以继承抽象类吗?
当然可以,毕竟其实抽象类就是多了一个关键词而已。
比如:spring框架源码中很多这样的存在。
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext
2.3 疑虑,抽象类可以new吗?
抽象类,还是class。那么能实例化吗?它是一种多态机制的体现机制。
答案是:不可以的?
2.4 抽象类化有构造函数,抽象类存在的构造函数的意义是什么?
- 给子类的构造函数通过super()方法给抽象类的私有赋值使用
2.5 抽象类可以继承普通类吗?
可以,但是一般不会这样做,有点本末倒置的味道。
2.6 抽象类可以继承抽象类吗?
- 可以,存在于spring框架源码大量的存在。--- 职责分担
- java中继承原则:单一继承,多实现
2.7 抽象类A 继承 抽象类B B的抽象方法,A一定覆盖吗?
不用,比如
public abstract class A extends B {
public void print(){}
public abstract void test1();
public abstract void test2();
}
package com.zheng.abstractdemo;
public abstract class B {
public void say(){}
public abstract void test3();
public abstract void test4();
}
package com.zheng.abstractdemo;
public class C extends A {
public void print(){}
@Override
public void test1() {
}
@Override
public void test2() {
}
@Override
public void test3() {
}
@Override
public void test4() {
}
}
从上面可以得到一个结论,如果具体子类继承了抽象类,就必须把所以的抽象方法进行覆盖。
这个jdk规定事情、这也是java多年来的毛病和问题?
2.8 总结
- 抽象类:其实比普通多了关键词abstract,而这个关键字只类中的方法起到约束作用,同时代表这个类不能实例化
- 抽象类,可以继承普通类,也可以继承抽象类,也实现接口。
3. 接口
3.1 JDK类的继承存在的问题
- java单一继承,多实现
- 如果多个抽象类中存在多个抽象方法,其子类必须要重写所有抽象方法。
接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。
- 接口中的方法定义默认为public abstract类型。
- 接口中的成员变量类型默认为public static final。都是静态常量
- jdk1.8之后允许定义默认方法和静态方法
3.2 接口的定义
接口:
public interface IUserService {
public static final int age = 10; // 这是一个常量
public static final String name = "222"; // 这是一个常量
String name2= "zhangsan"; // 这是一个常量
// 保存
void save();
// 修改
void update();
// 删除
void del();
// 查询
void query();
}
实现类:
package com.zheng.interfacedemo;
public class UserServiceImpl implements IUserService {
@Override
public void save() {
}
@Override
public void update() {
}
@Override
public void del() {
}
@Override
public void query() {
}
}
3.3 默认方法
定义默认方法,就是告诉子类你可以不用强制去重写这些方法,可以选择性的覆盖。
public interface IUserService {
int age = 10; // 这是一个常量
public static final String name = "222";
// 保存
default void save() {
}
// 修改
default void update() {
}
// 删除
default void del() {
}
// 查询
default void query() {
}
}
package com.zheng.interfacedemo;
public class UserServiceImpl implements IUserService {
@Override
public void update() {
}
@Override
public void query() {
}
}
3.4 静态方法
定义静态方法代表,这个方法可以通过接口类之间去调用,是公用的方法。
public interface IUserService {
int age = 10; // 这是一个常量
public static final String name = "222";
// 保存
default void save() {
}
// 修改
default void update() {
}
// 删除
default void del() {
}
// 查询
default void query() {
}
// 静态方法
static int getprice(){
return 1;
}
}
package com.zheng.interfacedemo;
public class UserServiceImpl implements IUserService {
@Override
public void update() {
int getprice = IUserService.getprice();
}
@Override
public void query() {
int getprice = IUserService.getprice();
}
}
3.5 接口是可以多继承接口的
public interface BeanFactoryAnnation extends BeanFactory,BeanFactoryClose{
/*解析注解类*/
void parseAnnation(Class clz);
}
3.6 Java为什么要存在接口?
在开发中,我们很多时候在框架代码中经常看到一个接口一个实现类的定义方式
。比如:
public interface IDemandService extends IService<Demand> {
/**
* 保存需求
* @param demand
* @return
*/
Demand saveDemand(Demand demand);
/**
* 查询分页需求信息
*
* @return
*/
Page<Demand> findDemandPage(int pageNo,int pageSize);
}
@Service
public class DemandServiceImpl extends ServiceImpl<DemandMapper, Demand> implements IDemandService {
@Override
public Demand saveDemand(Demand demand) {
new ClassPathXmlApplicationContext();
User user = UserThrealLocal.get();
if (user == null) {
throw new RuntimeException("用户没有登录!!");
}
demand.setUserid(user.getId());
demand.setUsername(user.getNickname());
demand.setAvatar(user.getAvatar());
demand.setStatus(1);
demand.setIsDelete(0);
demand.setIsover(1);
// saveOrUpdate 如果实体demand有id的话,就执行update,没有id就执行save
// saveOrUpdate 保存和修改成功以后返回的是boolean,但是实体中的id会自动填充回来
boolean b = this.saveOrUpdate(demand);
return b ? demand : null;
}
@Override
public Page<Demand> findDemandPage(int pageNo,int pageSize) {
//1 : 创建一个分页对象
Page<Demand> page = new Page<>(pageNo, pageSize);
//2 :创建查询条件
LambdaQueryWrapper<Demand> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//3:查询未删除的
lambdaQueryWrapper.eq(Demand::getIsDelete,0);
//4:设置排序条件
lambdaQueryWrapper.orderByDesc(Demand::getCreateTime);
//5:查询分页返回
return this.page(page, lambdaQueryWrapper);
}
}
由此可以看出,我们使用接口的目的
- 规范,约束 (一定是一个接口有多个子类的时候它价值就体现出来,也是多态的最佳体现)
- 方便后续的扩展
- Jdk动态代理,在框架大量的存在比如mybatis的mapper、Spring中的Aop
4. 接口和抽象类的区别
- 抽象类可以有构造方法,接口中不能有构造方法。
- 抽象类中可以有普通成员变量,接口中没有普通成员变量
- 抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法
- 抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
- 抽象类中可以包含静态方法,接口中不能包含静态方法,==JDK1.8以后允许==
- 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
- 一个类可以实现多个接口,但只能继承一个抽象类。 单一继承多实现
- Jdk1.8以后新增了默认方法default和static方法,慢慢替代抽象类的的含义,但是不能完全的消除抽象类。
5. 枚举
JDK1.5引入了新的类型——枚举。在 Java 中它虽然算个“小”功能,却给我的开发带来了“大”方便。
用法一:常量
在JDK1.5 之前,我们定义常量都是: public static final.... 。
现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法。
Java代码
public enum Color {
RED, GREEN, BLANK, YELLOW
}
用法二:switch
JDK1.6之前的switch语句只支持int,char,enum类型,使用枚举,能让我们的代码可读性更强。
Java代码
enum Signal {
GREEN, YELLOW, RED
}
public class TrafficLight {
Signal color = Signal.RED;
public void change() {
switch (color) {
case RED:
color = Signal.GREEN;
break;
case YELLOW:
color = Signal.RED;
break;
case GREEN:
color = Signal.YELLOW;
break;
}
}
}
用法三:向枚举中添加新方法
如果打算自定义自己的方法,那么必须在enum实例序列的最后添加一个分号。
而且 Java 要求必须先定义 enum 实例。
public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
// 普通方法
public static String getName(int index) {
for (Color c : Color.values()) {
if (c.getIndex() == index) {
return c.name;
}
}
return null;
}
// get set 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
用法四:覆盖枚举的方法
public enum Color {
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
//覆盖方法
@Override
public String toString() {
return this.index+"_"+this.name;
}
}
用法五:实现接口
所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类。
public interface Behaviour {
void print();
String getInfo();
}
public enum Color implements Behaviour{
RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
// 成员变量
private String name;
private int index;
// 构造方法
private Color(String name, int index) {
this.name = name;
this.index = index;
}
//接口方法
@Override
public String getInfo() {
return this.name;
}
//接口方法
@Override
public void print() {
System.out.println(this.index+":"+this.name);
}
}
用法六:使用接口组织枚举
Java代码
public interface Food {
enum Coffee implements Food{
BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO
}
enum Dessert implements Food{
FRUIT, CAKE, GELATO
}
}
/**
* 测试继承接口的枚举的使用(by 大师兄 or 大湿胸。)
*/
private static void testImplementsInterface() {
for (Food.DessertEnum dessertEnum : Food.DessertEnum.values()) {
System.out.print(dessertEnum + " ");
}
System.out.println();
//我这地方这么写,是因为我在自己测试的时候,把这个coffee单独到一个文件去实现那个food接口,而不是在那个接口的内部。
for (CoffeeEnum coffee : CoffeeEnum.values()) {
System.out.print(coffee + " ");
}
System.out.println();
//搞个实现接口,来组织枚举,简单讲,就是分类吧。如果大量使用枚举的话,这么干,在写代码的时候,就很方便调用啦。
//还有就是个“多态”的功能吧,
Food food = Food.DessertEnum.CAKE;
System.out.println(food);
food = CoffeeEnum.BLACK_COFFEE;
System.out.println(food);
}
用法七:关于枚举集合的使用
java.util.EnumSet和java.util.EnumMap是两个枚举集合。EnumSet保证集合中的元素不重复;EnumMap中的 key是enum类型,而value则可以是任意类型。关于这个两个集合的使用就不在这里赘述,可以参考JDK文档。
/**
* Java枚举用法测试
* <p>
* Created by lxk on 2016/12/15
*/
public class EnumTest {
public static void main(String[] args) {
forEnum();
useEnumInJava();
}
/**
* 循环枚举,输出ordinal属性;若枚举有内部属性,则也输出。(说的就是我定义的TYPE类型的枚举的typeName属性)
*/
private static void forEnum() {
for (SimpleEnum simpleEnum : SimpleEnum.values()) {
System.out.println(simpleEnum + " ordinal " + simpleEnum.ordinal());
}
System.out.println("------------------");
for (TYPE type : TYPE.values()) {
System.out.println("type = " + type + " type.name = " + type.name() + " typeName = " + type.getTypeName() + " ordinal = " + type.ordinal());
}
}
/**
* 在Java代码使用枚举
*/
private static void useEnumInJava() {
String typeName = "f5";
TYPE type = TYPE.fromTypeName(typeName);
if (TYPE.BALANCE.equals(type)) {
System.out.println("根据字符串获得的枚举类型实例跟枚举常量一致");
} else {
System.out.println("大师兄代码错误");
}
}
/**
* 季节枚举(不带参数的枚举常量)这个是最简单的枚举使用实例
* Ordinal 属性,对应的就是排列顺序,从0开始。
*/
private enum SimpleEnum {
SPRING,
SUMMER,
AUTUMN,
WINTER
}
/**
* 常用类型(带参数的枚举常量,这个只是在书上不常见,实际使用还是很多的,看懂这个,使用就不是问题啦。)
*/
private enum TYPE {
FIREWALL("firewall"),
SECRET("secretMac"),
BALANCE("f5");
private String typeName;
TYPE(String typeName) {
this.typeName = typeName;
}
/**
* 根据类型的名称,返回类型的枚举实例。
*
* @param typeName 类型名称
*/
public static TYPE fromTypeName(String typeName) {
for (TYPE type : TYPE.values()) {
if (type.getTypeName().equals(typeName)) {
return type;
}
}
return null;
}
public String getTypeName() {
return this.typeName;
}
}
}
总结
enum这个关键字,可以理解为跟class差不多,这也个单独的类。可以看到,上面的例子里面有属性,有构造方法,有getter,
也可以有setter,但是一般都是构造传参数。还有其他自定义方法。
那么在这些东西前面的,以逗号隔开的,最后以分号结尾的,这部分叫做,这个枚举的实例。
也可以理解为,class new 出来的实例对象。这下就好理解了。
只是,class,new对象,可以自己随便new,想几个就几个,
而这个enum关键字,他就不行,他的实例对象,只能在这个enum里面体现。
也就是说,他对应的实例是有限的。这也就是枚举的好处了,限制了某些东西的范围,
举个栗子:一年四季,只能有春夏秋冬,你要是字符串表示的话,那就海了去了,
但是,要用枚举类型的话,你在enum的大括号里面把所有的选项,全列出来,那么这个季节的属性,对应的值,只能在里面挑。
不能有其他的。
6. 枚举的实际应用场景
HttpStatus
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.http;
import org.springframework.lang.Nullable;
public enum HttpStatus {
CONTINUE(100, "Continue"),
SWITCHING_PROTOCOLS(101, "Switching Protocols"),
PROCESSING(102, "Processing"),
CHECKPOINT(103, "Checkpoint"),
OK(200, "OK"),
CREATED(201, "Created"),
ACCEPTED(202, "Accepted"),
NON_AUTHORITATIVE_INFORMATION(203, "Non-Authoritative Information"),
NO_CONTENT(204, "No Content"),
RESET_CONTENT(205, "Reset Content"),
PARTIAL_CONTENT(206, "Partial Content"),
MULTI_STATUS(207, "Multi-Status"),
ALREADY_REPORTED(208, "Already Reported"),
IM_USED(226, "IM Used"),
MULTIPLE_CHOICES(300, "Multiple Choices"),
MOVED_PERMANENTLY(301, "Moved Permanently"),
FOUND(302, "Found"),
/** @deprecated */
@Deprecated
MOVED_TEMPORARILY(302, "Moved Temporarily"),
SEE_OTHER(303, "See Other"),
NOT_MODIFIED(304, "Not Modified"),
/** @deprecated */
@Deprecated
USE_PROXY(305, "Use Proxy"),
TEMPORARY_REDIRECT(307, "Temporary Redirect"),
PERMANENT_REDIRECT(308, "Permanent Redirect"),
BAD_REQUEST(400, "Bad Request"),
UNAUTHORIZED(401, "Unauthorized"),
PAYMENT_REQUIRED(402, "Payment Required"),
FORBIDDEN(403, "Forbidden"),
NOT_FOUND(404, "Not Found"),
METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
NOT_ACCEPTABLE(406, "Not Acceptable"),
PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required"),
REQUEST_TIMEOUT(408, "Request Timeout"),
CONFLICT(409, "Conflict"),
GONE(410, "Gone"),
LENGTH_REQUIRED(411, "Length Required"),
PRECONDITION_FAILED(412, "Precondition Failed"),
PAYLOAD_TOO_LARGE(413, "Payload Too Large"),
/** @deprecated */
@Deprecated
REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large"),
URI_TOO_LONG(414, "URI Too Long"),
/** @deprecated */
@Deprecated
REQUEST_URI_TOO_LONG(414, "Request-URI Too Long"),
UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"),
REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested range not satisfiable"),
EXPECTATION_FAILED(417, "Expectation Failed"),
I_AM_A_TEAPOT(418, "I'm a teapot"),
/** @deprecated */
@Deprecated
INSUFFICIENT_SPACE_ON_RESOURCE(419, "Insufficient Space On Resource"),
/** @deprecated */
@Deprecated
METHOD_FAILURE(420, "Method Failure"),
/** @deprecated */
@Deprecated
DESTINATION_LOCKED(421, "Destination Locked"),
UNPROCESSABLE_ENTITY(422, "Unprocessable Entity"),
LOCKED(423, "Locked"),
FAILED_DEPENDENCY(424, "Failed Dependency"),
TOO_EARLY(425, "Too Early"),
UPGRADE_REQUIRED(426, "Upgrade Required"),
PRECONDITION_REQUIRED(428, "Precondition Required"),
TOO_MANY_REQUESTS(429, "Too Many Requests"),
REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large"),
UNAVAILABLE_FOR_LEGAL_REASONS(451, "Unavailable For Legal Reasons"),
INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
NOT_IMPLEMENTED(501, "Not Implemented"),
BAD_GATEWAY(502, "Bad Gateway"),
SERVICE_UNAVAILABLE(503, "Service Unavailable"),
GATEWAY_TIMEOUT(504, "Gateway Timeout"),
HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version not supported"),
VARIANT_ALSO_NEGOTIATES(506, "Variant Also Negotiates"),
INSUFFICIENT_STORAGE(507, "Insufficient Storage"),
LOOP_DETECTED(508, "Loop Detected"),
BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded"),
NOT_EXTENDED(510, "Not Extended"),
NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required");
private final int value;
private final String reasonPhrase;
private HttpStatus(int value, String reasonPhrase) {
this.value = value;
this.reasonPhrase = reasonPhrase;
}
public int value() {
return this.value;
}
public String getReasonPhrase() {
return this.reasonPhrase;
}
public HttpStatus.Series series() {
return HttpStatus.Series.valueOf(this);
}
public boolean is1xxInformational() {
return this.series() == HttpStatus.Series.INFORMATIONAL;
}
public boolean is2xxSuccessful() {
return this.series() == HttpStatus.Series.SUCCESSFUL;
}
public boolean is3xxRedirection() {
return this.series() == HttpStatus.Series.REDIRECTION;
}
public boolean is4xxClientError() {
return this.series() == HttpStatus.Series.CLIENT_ERROR;
}
public boolean is5xxServerError() {
return this.series() == HttpStatus.Series.SERVER_ERROR;
}
public boolean isError() {
return this.is4xxClientError() || this.is5xxServerError();
}
public String toString() {
return this.value + " " + this.name();
}
public static HttpStatus valueOf(int statusCode) {
HttpStatus status = resolve(statusCode);
if (status == null) {
throw new IllegalArgumentException("No matching constant for [" + statusCode + "]");
} else {
return status;
}
}
@Nullable
public static HttpStatus resolve(int statusCode) {
HttpStatus[] var1 = values();
int var2 = var1.length;
for(int var3 = 0; var3 < var2; ++var3) {
HttpStatus status = var1[var3];
if (status.value == statusCode) {
return status;
}
}
return null;
}
public static enum Series {
INFORMATIONAL(1),
SUCCESSFUL(2),
REDIRECTION(3),
CLIENT_ERROR(4),
SERVER_ERROR(5);
private final int value;
private Series(int value) {
this.value = value;
}
public int value() {
return this.value;
}
public static HttpStatus.Series valueOf(HttpStatus status) {
return valueOf(status.value);
}
public static HttpStatus.Series valueOf(int statusCode) {
HttpStatus.Series series = resolve(statusCode);
if (series == null) {
throw new IllegalArgumentException("No matching constant for [" + statusCode + "]");
} else {
return series;
}
}
@Nullable
public static HttpStatus.Series resolve(int statusCode) {
int seriesCode = statusCode / 100;
HttpStatus.Series[] var2 = values();
int var3 = var2.length;
for(int var4 = 0; var4 < var3; ++var4) {
HttpStatus.Series series = var2[var4];
if (series.value == seriesCode) {
return series;
}
}
return null;
}
}
}