继承(下)
继承中构造方法的访问特点
子类在初始化之前必须先初始化父类。即在调用子类的构造方法之前必须调用父类的构造方法。因为子类里面的有部分属性和方法使用的父类的,必须将父类的方法和属性初始化,子类才可以拿来使用(有父才有子)
我们的父类中会有JVM隐藏添加的空参构造方法
class Person{
//隐藏构造方法 public Person(){}
}
子类中隐藏构造方法
class Student extends Person{
// public Student(){隐藏构造方法
super();
}
当我们没有写空参构造方法时,JVM会默认添加上述空参构造。如果自己写了空参构造JVM则取消默认添加。每一个构造方法中默认隐藏代码super()
注意:当类创建了构造方法,JVM将默认不提供默认空参构造方法
**子类的所以构造方法都是默认调用空参构造super()
- 总结
关于this()必须放在构造方法第一行原因的理解:
-
在Java中,this() 是一个特殊的构造方法调用语句,用于在当前类的构造方法中调用其他重载的构造方法。而且,this() 必须作为构造方法内的第一条语- 句,这是因为以下两个原因:
-
初始化顺序:在构造方法中,对象的成员变量需要进行初始化。如果某个构造方法中使用了 this() 来调用其他构造方法,那么被调用的构造方法会先执行,进行相应的成员变量赋值操作。因此,this() 语句必须放在构造方法的第一行,确保在当前构造方法之前没有执行其他的成员变量初始化代码。
内存图解
- 总结
信息管理系统--集成改进
开发原则:开闭原则:对拓展内容开放,对修改内容关闭
对于想拓展一些新的内容开放,但是不能修改代码的原有内容
举例:我们发现StudentControlled类中关于对学生对象的封装,有更好的方式(原来使用空参构造,然后set方法赋值),现在有了新 的方式
我们可以复制一份StduentControllr文件,在这个文件的基础上进行修改,而不修改原有的文件
- 想要
- 我们复制了原来的StudentController类为OtherStudentController类,并修改OtherStuentController类中的代码()
- 修改一下入口类里面的类就可以使用新的类了,而原有的类可以不必修改或者删除了
但是我们可以看到我们的StudentControler类和OtherStudentController类有大量相同的代码,只有键盘录入学生对象有差异,这时我们就可以将StudentController类和OtherStudentController类向上抽取成一个BeseStudentControllerl父类,在子类中重写键盘录入录入学生对象的方法就可以了
- 信息管理系统改进详细步骤
- 子类StudentController
package com.itheima.edu.info.manager.controller;
import com.itheima.edu.info.manager.domain.Student;
import com.itheima.edu.info.manager.service.StudentService;
import java.util.Scanner;
//客服接待,和用户打交道
public class StudentController extends BaseStudentController {
private Scanner sc = new Scanner(System.in);
//键盘录入id
//键盘录入学生信息
@Override
public Student inputStudentInfo(String id){
System.out.println("请输入学生姓名:");
final String name = sc.next();
System.out.println("请输入学生年龄:");
final int age = sc.nextInt();
System.out.println("请输入学生生日:");
final String birthday = sc.next();
//2.将学生信息封装成对象
Student student = new Student();
student.setId(id);
student.setName(name);
student.setAge(age);
student.setBirthday(birthday);
return student;
}
}
- 子类OtherStudentController
package com.itheima.edu.info.manager.controller;
import com.itheima.edu.info.manager.domain.Student;
import com.itheima.edu.info.manager.service.StudentService;
import java.util.Scanner;
//客服接待,和用户打交道
public class OtherStudentController extends BaseStudentController {
private Scanner sc = new Scanner(System.in);
//键盘录入学生信息
@Override
public Student inputStudentInfo(String id){
System.out.println("请输入学生姓名:");
final String name = sc.next();
System.out.println("请输入学生年龄:");
final int age = sc.nextInt();
System.out.println("请输入学生生日:");
final String birthday = sc.next();
//2.将学生信息封装成对象
Student student = new Student(id, name, age, birthday);//对象封装对象进行修改
return student;
}
}
- 父类BaseStudentController
package com.itheima.edu.info.manager.controller;
import com.itheima.edu.info.manager.domain.Student;
import com.itheima.edu.info.manager.service.StudentService;
import java.util.Scanner;
//客服接待,和用户打交道
public class BaseStudentController {
private Scanner sc = new Scanner(System.in);
private StudentService studentService = new StudentService();
//开启学生管理系统并展示菜单
public void start() {
System.out.println("--------------欢迎来到学生管理系统..............................");
studentLoop: while (true) {
System.out.println("请输入你的选择:1.添加学生 2.删除学生 3.修改学生 4.查看学生 5.退出");
final String choice = sc.next();
switch (choice) {
case "1":
addStudent();
break;
case "2":
// System.out.println("删除学生");
deleteStudentById();
break;
case "3":
// System.out.println("修改学生");
updateStudent();
break;
case "4":
//System.out.println("查看学生");
findStudent();
break;
case "5":
System.out.println("欢迎使用学生管理系统,再见");
break studentLoop;//这里需要跳转到总菜单
default:
System.out.println("输入有误");
}
}
}
//修改学生
public void updateStudent() {
final String id = inputStudentId();
//3.录入新的学生信息,并封装成对象
Student student = inputStudentInfo(id);
//4.调用studentService中的updateStudent方法修改学生信息,修改学生并提示修改成功
studentService.updateStudent(id,student);
System.out.println("修改成功");
}
public void deleteStudentById() {
final String id = inputStudentId();
//3.调用业务员中的deleteStudentById方法根据id删除学生
studentService.deleteStudentById(id);
//4.提示删除成功
System.out.println("删除成功");
}
//查看学生
public void findStudent() {
//1.调用业务员中的获取方法,得到学生的对象数组
Student[] students = studentService.findStudent();
//2.判断数组的内存地址,是否为null
if (students == null) {
System.out.println("查无信息,请添加后重试");
return;
}
//3.遍历数组,获取学生信息并打印在控制台上
System.out.println("学号\t\t姓名\t年龄\t生日");
for (Student student : students) {
if (student != null) {
System.out.println(student.getId() + "\t" + student.getName() + "\t\t" + student.getAge() + "\t\t" + student.getBirthday());
}
}
}
public void addStudent() {
//1. 键盘接收学生信息
StudentService studentService = new StudentService();
String id;
while (true) {
System.out.println("请输入学生id:");
id = sc.next();
//判断id是否重复
boolean reason = studentService.isExact(id);
if (!reason) {//如果id没有重复则跳出循环
break;
} else {
System.out.println("id已存在,请重新输入");
}
}
//键盘录入学生信息
final Student student = inputStudentInfo(id);
//3.将学生对象传递给StudentService(业务员)中的addStudent方法(调用StudentService中的addStudent方法来实现功能)
boolean reason = studentService.addStudent(student);
//4.根据返回的boolean类型结果,在控制台打印成功/失败
if (reason) {
System.out.println("添加成功");
} else {
System.out.println("添加失败");
}
}
//键盘录入id
public String inputStudentId(){
String id;
while (true) {
System.out.println("请输入学生id:");
id = sc.next();
//判断id是否重复
boolean reason = studentService.isExact(id);
if (reason) {//如果id重复则跳出循环
break;
} else {
System.out.println("id不存在,请重新输入");
}
}
return id;
}
//键盘录入学生信息
public Student inputStudentInfo(String id){//子类将重写该方法
return null;
}
}
抽象类入门
- 举例理解抽象类和抽象方法
继承将类中相同的内容向上抽取成抽象类,而在子类中留下其特有的内容。对于父类中无法描述其逻辑的方法可以定义成抽象方法,而抽象方法必须存活在抽象类中
- 抽象方法和抽象类的定义格式
此时信息管理系统分析:
1.Student类和老师类有相同的特征向上抽取为Person类
2.(修改新创建类中inputTeacherInfo方法被修改)根据开闭原则修改StudentController类,新创建一个OtherStudentController类(在OtherStudentController类中修改,StudentController类不变)
3.自然想到将StudentController类和OtherStudentController类抽取成一个父类BaseStudentController
4.发现在子类中都重写了InputStudentInfo方法,父类中的InputStudentInfo无法表示,所以父类中的该方法写成抽象方法,BaseStudentController写成抽象类
可以发现这一步步优化都是合情合理,都是很自然的
抽象类注意事项
1.抽象了必须存在构造方法
- 因为子类继承抽象类,子类的构造方法都是隐藏自动调用父类的无参构造super()(没有就矛盾了)
2.抽象类不能创建对象 - 抽象类必有抽象方法,抽象方法没有方法体,如果抽象类可以实例化对象,然后调用抽象方法将没有任何意义
3.抽象类的子类 - 选择
- A:必须重写抽象类所以的抽象方法
- B:子类也为抽象类
4.抽象类中的方法
- 有抽象方法的类必须是抽象类(抽象类中也可以没有抽象方法)
- 总结
模板设计模式
设计模式是一个良好的编码风格
关于模板:模板可以认为了有一部分内容是固定不变的,而有一部分内容则是每一个人都不同。如作文中标题和结尾是固定的,而正文部分则是因人而异。我们的抽象类就可以这样来设计模板:将固定的内容设计成一般方法,子类直接继承,而将不同的内容设计成抽象方法,子类进行抽象。此时我们的抽象父类就是一个模板,而我们的子类就是根据模板衍生出来的不同的设计作品
- 模板类
package com.itheima.test;
//作文的模板类
public abstract class CompositionTemplate {
public void write(){
System.out.println("<<我的爸爸>>");//标题(固定)
body();//正文(由子类决定)
System.out.println("这就是我的爸爸");//结尾(固定)
}
public abstract void body();
}
- 子类
package com.itheima.test;
//现在Tom要用模板写作文
public class Tom extends CompositionTemplate {
@Override
public void body() {
System.out.println("那是一个秋天,爸爸骑车带我上学,一切都是那样的美好" );
}
}
- 测试类
package com.itheima.test;
public class Test {
public static void main(String[] args) {
Tom tom = new Tom();
tom.write();
}
}
- 总结
final关键字
-
存在的问题
-
解决
-
此时将会强制无法重写
建议以后的模板方法都使用final修饰
-
final修饰类的应用场景
假如你写好了一个类,这个类中所有的方法都是核心方法,不希望子类去重写这个类的方法。解决方法1.在类中的所有的方法上面加上final2.将类定义成final(更简便) -
final修饰变量(基本变量和引用变量)
信息管理系统-- 抽象类的改进
- 修改点
修改点:在此之前我们的BaseStudentController类已经为抽象类, 很显然我们只希望抽象方法被抽象而,其他方法不变。这符合模板设计模式。所有我们需要保证模板方法不被重写,所有我们可以将模板方法定义为final
代码块
-
信息系统存在的问题(使用后面学的静态代码块实现优化)
-
构造方法和构造代码块执行顺序
-
构造代码块的作用
-
举例(构造代码块的作用): 每次创建一个对象前都要打印一句“好好学习”
-
没有使用构造代码块的时
package com.itheima.test1;
public class Student {
public String name;
//空参构造
public Student(){
System.out.println("好好学习");
}
//全参构造
public Student(String name){
System.out.println("好好学习");
this.name=name;
}
}
- 使用构造代码块进行优化
package com.itheima.test1;
public class Student {
public String name;
//构造代码块
{
System.out.println("好好学习");
}
//空参构造
public Student(){
}
//全参构造
public Student(String name){
this.name=name;
}
}
将多个构造方法中相同的代码抽取出来,提高的代码的复用性
- 静态代码块
- 代码块总结
信息管理系统---代码块改进
- 改进点
- StudentDao改进
- 1.因为我们是对数据进行操作的,所以一定是在Dao类中进行操作,所以是在StudentDao中创建静态代码块
- 2.当我们执行程序入口(加载InfoManageEntry类),点击学生管理(加载StudentService),点击查看学生,调用StduentDao中的FindStudent方法,此时加载StdudntDao类,学生数据被初始化**
可以发现我们的调用是一级一级进行,然后一级一级返回的