类与类之间产生关系,
子类可以直接调用父类中的非私有成员
extends
实现格式:
public class 子类 extends 父类 {}
范例:
//父类
public class Fu {
}
//子类
public class Zi extends Fu {
}
示例代码
//父类
public class Fu {
public void show() {
System.out.println("show方法被调用");
}
}
//子类
public class Zi extends Fu {
public void method() {
System.out.println("method方法被调用");
}
}
//测试类
public class Demo {
public static void main(String[] args) {
//创建对象,调用方法
Fu f = new Fu();
f.show();
Zi z = new Zi();
z.method();
z.show();
}
}
复用性
维护性
多态的前提
总结:
提升了代码 (复用性、维护性) ,是多态的前提
耦合
)总结:
降低了代码的灵活性,增强了代码的耦合性
什么时候使用继承?
总结:
谁是谁的一种
。例如:老师和学生是人的一种,那人就是父类,学生和老师就是子类
学生类:姓名,年龄) ———————————》人类:姓名,年龄 老师类:姓名,年龄)
项目经理:姓名,工号,工资) ——————————————》员工:姓名,工号,工资 程序员: 姓名,工号,工资)
在Java语言中,类
只支持单继承,不支持多继承,但可以多层继承。
public class C extends A,B{
}
Java中类支持多层继承
package com.itheima.day02;
/**
* @Author: ✎﹏ Sunflower丶
*/
public class Granddad {
public void drink() {
System.out.println("爷爷爱喝酒");
}
}
package com.itheima.day02;
/**
@Author: ✎﹏ Sunflower丶
*/
public class Father extends Granddad {
public void smoke() {
System.out.println("爸爸爱抽烟");
}
}
package com.itheima.day02;
/**
* @Author: ✎﹏ Sunflower丶
*/
public class Mother {
public void dance() {
System.out.println("妈妈爱跳舞");
}
}
package com.itheima.day02;
/**
* @Author: ✎﹏ Sunflower丶
*/
public class Son extends Father {
// 此时,Son类中就同时拥有drink方法以及smoke方法
}
问题:
为什么不支持多继承?
先举一个多重继承的例子,我们定义一个动物(类)既是狗(父类1)也是猫(父类2),两个父类都有“叫”这个方法。那么当我们调用“叫”这个方法时,它就不知道是狗叫还是猫叫了,这就是多重继承的冲突。因此,Java不支持多继承。
在子类方法中访问一个变量,采用就近原则。
示例代码
//只是进行演示,不建议在一个文件里写多个类。
class Fu {
int num = 10;
}
class Zi {
int num = 20;
public void show(){
int num = 30;
System.out.println(num);
System.out.println(this.num);
System.out.println(super.num);
}
}
public class Demo1 {
public static void main(String[] args) {
Zi z = new Zi();
z.show(); // 输出show方法中的局部变量30
}
}
注意: 如果子父类中,出现了重名的成员变量,通过就近原则,会优先于子类。 如果一定要访问父类的,那么可以通过super关键字,进行区分。
this
:代表子类对象的引用super
:代表父类存储空间的标识,换一句话说是代表父类对象的引用
关键字 | 成员变量 | 成员方法 | 构造方法 |
---|---|---|---|
this | this.成员变量 - 访问本内成员变量 | this.成员方法() - 访问本内成员方法 | this(...) - 访问本内构造方法 |
super | super.成员变量 - 访问父类成员变量 | super.成员方法() - 访问父类成员方法 | super(...) - 访问父类构造方法 |
通过子类对象访问一个方法
子类中所有的构造方法都会默认的访问父类的无参构造方法
为什么会这么访问?
总结:子类初始化之前,一定会先完成父类初始化
怎么初始化?
super();
,若没指定底层也会自动添加。 问题:如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?
this(…)super(…) 必须放在构造方法的第一行有效语句,并且二者不能共存
注意:如果我们编写的类,没有手动指定父类,系统也会自动继承Object
(Java继承体系中的最顶层父类----鼻祖)
方法重写的应用场景
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
package com.itheima.day02;
/**
* @Author: ✎﹏ Sunflower丶
*/
public class Fu {
private void show() {
System.out.println("Fu中show()方法被调用");
}
void method() {
System.out.println("Fu中method()方法被调用");
}
}
package com.itheima.day02;
/**
* @Author: ✎﹏ Sunflower丶
*/
public class Zi extends Fu {
/* 编译【出错】,子类不能重写父类私有的方法*/
@Override
private void show() {
System.out.println("Zi中show()方法被调用");
}
/* 编译【出错】,子类重写父类方法的时候,访问权限需要大于等于父类 */
@Override
private void method() {
System.out.println("Zi中method()方法被调用");
}
/* 编译【通过】,子类重写父类方法的时候,访问权限需要大于等于父类 */
@Override
public void method() {
System.out.println("Zi中method()方法被调用");
}
}
面试题
!!!)多态性表现上:
(多态:同一个对象,在不同时刻表现出来的不同形态)后续会详细介绍。定义上的不同:
规则上的不同:
修饰符 | 同一个类中 | 同一个包中子类无关类 | 不同包的子类 | 不同包的无关类 |
---|---|---|---|---|
private | √ | |||
默认 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
需求
把学生类和老师类共性的内容向上抽取,抽取到出一个 Person 父类,让学生类和老师类继承 Person 类
实现步骤
代码实现
Person类及学生类和老师类
package com.itheima.edu.info.manager.domain;
/**
* @Author: ✎﹏ Sunflower丶
*/
public class Person {
private String id;
private String name;
private String age;
private String birthday;
public Person() {
}
public Person(String id, String name, String age, String birthday) {
this.id = id;
this.name = name;
this.age = age;
this.birthday = birthday;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
}
// Student类
public class Student extends Person {
public Student() {
}
public Student(String id, String name, String age, String birthday) {
super(id, name, age, birthday);
}
}
// Teacher类
public class Teacher extends Person {
public Teacher() {
}
public Teacher(String id, String name, String age, String birthday) {
super(id, name, age, birthday);
}
}
BaseStudentController类
public abstract class BaseStudentController {
// 业务员对象
private StudentService studentService = new StudentService();
private Scanner sc = new Scanner(System.in);
// 开启学生管理系统, 并展示学生管理系统菜单
public void start() {
//Scanner sc = new Scanner(System.in);
studentLoop:
while (true) {
System.out.println("--------欢迎来到 <学生> 管理系统--------");
System.out.println("请输入您的选择: 1.添加学生 2.删除学生 3.修改学生 4.查看学生 5.退出");
String choice = sc.next();
switch (choice) {
case "1":
// System.out.println("添加");
addStudent();
break;
case "2":
// System.out.println("删除");
deleteStudentById();
break;
case "3":
// System.out.println("修改");
updateStudent();
break;
case "4":
// System.out.println("查询");
findAllStudent();
break;
case "5":
System.out.println("感谢您使用学生管理系统, 再见!");
break studentLoop;
default:
System.out.println("您的输入有误, 请重新输入");
break;
}
}
}
// 修改学生方法
public void updateStudent() {
String updateId = inputStudentId();
Student newStu = inputStudentInfo(updateId);
studentService.updateStudent(updateId, newStu);
System.out.println("修改成功!");
}
// 删除学生方法
public void deleteStudentById() {
String delId = inputStudentId();
// 3. 调用业务员中的deleteStudentById根据id, 删除学生
studentService.deleteStudentById(delId);
// 4. 提示删除成功
System.out.println("删除成功!");
}
// 查看学生方法
public void findAllStudent() {
// 1. 调用业务员中的获取方法, 得到学生的对象数组
Student[] stus = studentService.findAllStudent();
// 2. 判断数组的内存地址, 是否为null
if (stus == null) {
System.out.println("查无信息, 请添加后重试");
return;
}
// 3. 遍历数组, 获取学生信息并打印在控制台
System.out.println("学号\t\t姓名\t年龄\t生日");
for (int i = 0; i < stus.length; i++) {
Student stu = stus[i];
if (stu != null) {
System.out.println(stu.getId() + "\t" + stu.getName() + "\t" + stu.getAge() + "\t\t" + stu.getBirthday());
}
}
}
// 添加学生方法
public void addStudent() {
// StudentService studentService = new StudentService();
// 1. 键盘接收学生信息
String id;
while (true) {
System.out.println("请输入学生id:");
id = sc.next();
boolean flag = studentService.isExists(id);
if (flag) {
System.out.println("学号已被占用, 请重新输入");
} else {
break;
}
}
Student stu = inputStudentInfo(id);
// 3. 将学生对象,传递给StudentService(业务员)中的addStudent方法
boolean result = studentService.addStudent(stu);
// 4. 根据返回的boolean类型结果, 在控制台打印成功\失败
if (result) {
System.out.println("添加成功");
} else {
System.out.println("添加失败");
}
}
// 键盘录入学生id
public String inputStudentId() {
String id;
while (true) {
System.out.println("请输入学生id:");
id = sc.next();
boolean exists = studentService.isExists(id);
if (!exists) {
System.out.println("您输入的id不存在, 请重新输入:");
} else {
break;
}
}
return id;
}
// 键盘录入学生信息
// 开闭原则: 对扩展内容开放, 对修改内容关闭
public Student inputStudentInfo(String id){
return null;
}
}
StudentController类
public class StudentController extends BaseStudentController {
private Scanner sc = new Scanner(System.in);
// 键盘录入学生信息
// 开闭原则: 对扩展内容开放, 对修改内容关闭
@Override
public Student inputStudentInfo(String id) {
System.out.println("请输入学生姓名:");
String name = sc.next();
System.out.println("请输入学生年龄:");
String age = sc.next();
System.out.println("请输入学生生日:");
String birthday = sc.next();
Student stu = new Student();
stu.setId(id);
stu.setName(name);
stu.setAge(age);
stu.setBirthday(birthday);
return stu;
}
}
OtherStudentController类
public class OtherStudentController extends BaseStudentController {
private Scanner sc = new Scanner(System.in);
// 键盘录入学生信息
// 开闭原则: 对扩展内容开放, 对修改内容关闭
@Override
public Student inputStudentInfo(String id) {
System.out.println("请输入学生姓名:");
String name = sc.next();
System.out.println("请输入学生年龄:");
String age = sc.next();
System.out.println("请输入学生生日:");
String birthday = sc.next();
Student stu = new Student(id,name,age,birthday);
return stu;
}
}
抽象方法:将共性的行为 (方法) 抽取到父类之后
,发现该方法的实现逻辑无法在父类中给出具体明确
,该方法就可以定义为抽象方法。
public
abstract
返回值类型 方法名 (参数列表);
public abstract void method();
抽象类:如果一个类中存在抽象方法
,那么该类就必须声明为抽象类
。
public
abstract
class 类名{}
public abstract class Student{}
抽象类和抽象方法必须使用 abstract 关键字修饰
//抽象类的定义
public abstract class 类名 {}
//抽象方法的定义
public abstract void eat();
案例需求
定义猫类(Cat)和狗类(Dog)
猫类成员方法:eat(猫吃鱼)drink(喝水…)
狗类成员方法:eat(狗吃肉)drink(喝水…)
实现步骤
代码实现
public abstract class Animal {
public void drink(){
System.out.println("喝水");
}
public Animal(){
}
public abstract void eat();
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃肉");
}
}
public static void main(String[] args) {
Dog d = new Dog();
d.eat();
d.drink();
Cat c = new Cat();
c.drink();
c.eat();
//Animal a = new Animal();
//a.eat();
}
设计模式
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
模板设计模式
把抽象类整体就可以看做成一个模板,模板中不能决定的东西定义成抽象方法 让使用模板的类(继承抽象类的类)去重写抽象方法实现需求
模板设计模式的优势
模板已经定义了通用结构,使用者只需要关心自己需要实现的功能即可
示例代码
模板类
/*
作文模板类
*/
public abstract class CompositionTemplate {
public final void write(){
System.out.println("<<我的爸爸>>");
body();
System.out.println("啊~ 这就是我的爸爸");
}
public abstract void body();
}
实现类A
public class Tom extends CompositionTemplate {
@Override
public void body() {
System.out.println("那是一个秋天, 风儿那么缠绵,记忆中, " +
"那天爸爸骑车接我放学回家,我的脚卡在了自行车链当中, 爸爸蹬不动,他就站起来蹬...");
}
}
实现类B
public class Tony extends CompositionTemplate {
@Override
public void body() {
}
/*public void write(){
}*/
}
测试类
public class Test {
public static void main(String[] args) {
Tom t = new Tom();
t.write();
}
}
final
关键字是最终的意思,可以修饰方法,变量,类
final
修饰的特点
常量
,不能再次被赋值最终方法
,不能被重写最终类
,不能被继承
final修饰变量
:
常量命名规范
:
被final修饰的变量是常量。如果常量名只有一个单词,那么字母全部大写。如果常量名是由多个单词,那么全部大写,不过每个单词用下划线(_
)隔开。
范例:
public static void main(String[] args){
final int NUMBER = 10; //正确
NUMBER = 20; //错误
final int ONE_WORD;
ONE_WORD = 10; //正确
ONE_WORD = 10; //错误
final Student s = new Student(23);
s = new Student(24); // 错误
s.setAge(24); // 正确
}
需求
代码实现
BaseStudentController类
public abstract class BaseStudentController {
// 业务员对象
private StudentService studentService = new StudentService();
private Scanner sc = new Scanner(System.in);
// 开启学生管理系统, 并展示学生管理系统菜单
public final void start() {
//Scanner sc = new Scanner(System.in);
studentLoop:
while (true) {
System.out.println("--------欢迎来到 <学生> 管理系统--------");
System.out.println("请输入您的选择: 1.添加学生 2.删除学生 3.修改学生 4.查看学生 5.退出");
String choice = sc.next();
switch (choice) {
case "1":
// System.out.println("添加");
addStudent();
break;
case "2":
// System.out.println("删除");
deleteStudentById();
break;
case "3":
// System.out.println("修改");
updateStudent();
break;
case "4":
// System.out.println("查询");
findAllStudent();
break;
case "5":
System.out.println("感谢您使用学生管理系统, 再见!");
break studentLoop;
default:
System.out.println("您的输入有误, 请重新输入");
break;
}
}
}
// 修改学生方法
public final void updateStudent() {
String updateId = inputStudentId();
Student newStu = inputStudentInfo(updateId);
studentService.updateStudent(updateId, newStu);
System.out.println("修改成功!");
}
// 删除学生方法
public final void deleteStudentById() {
String delId = inputStudentId();
// 3. 调用业务员中的deleteStudentById根据id, 删除学生
studentService.deleteStudentById(delId);
// 4. 提示删除成功
System.out.println("删除成功!");
}
// 查看学生方法
public final void findAllStudent() {
// 1. 调用业务员中的获取方法, 得到学生的对象数组
Student[] stus = studentService.findAllStudent();
// 2. 判断数组的内存地址, 是否为null
if (stus == null) {
System.out.println("查无信息, 请添加后重试");
return;
}
// 3. 遍历数组, 获取学生信息并打印在控制台
System.out.println("学号\t\t姓名\t年龄\t生日");
for (int i = 0; i < stus.length; i++) {
Student stu = stus[i];
if (stu != null) {
System.out.println(stu.getId() + "\t" + stu.getName() + "\t" + stu.getAge() + "\t\t" + stu.getBirthday());
}
}
}
// 添加学生方法
public final void addStudent() {
// StudentService studentService = new StudentService();
// 1. 键盘接收学生信息
String id;
while (true) {
System.out.println("请输入学生id:");
id = sc.next();
boolean flag = studentService.isExists(id);
if (flag) {
System.out.println("学号已被占用, 请重新输入");
} else {
break;
}
}
Student stu = inputStudentInfo(id);
// 3. 将学生对象,传递给StudentService(业务员)中的addStudent方法
boolean result = studentService.addStudent(stu);
// 4. 根据返回的boolean类型结果, 在控制台打印成功\失败
if (result) {
System.out.println("添加成功");
} else {
System.out.println("添加失败");
}
}
// 键盘录入学生id
public String inputStudentId() {
String id;
while (true) {
System.out.println("请输入学生id:");
id = sc.next();
boolean exists = studentService.isExists(id);
if (!exists) {
System.out.println("您输入的id不存在, 请重新输入:");
} else {
break;
}
}
return id;
}
// 键盘录入学生信息
// 开闭原则: 对扩展内容开放, 对修改内容关闭
public abstract Student inputStudentInfo(String id);
}
在Java中,使用{ }
括起来的代码被称为代码块
局部代码块
示例代码
public class Test {
public static void main(String[] args) {
{
int a = 10;
System.out.println(a);
}
// System.out.println(a);
}
}
构造代码块
示例代码
public class Test {
public static void main(String[] args) {
Student stu1 = new Student();
Student stu2 = new Student(10);
}
}
class Student {
{
System.out.println("好好学习");
}
public Student(){
System.out.println("空参数构造方法");
}
public Student(int a){
System.out.println("带参数构造方法...........");
}
}
静态代码块
示例代码
public class Test {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person(10);
}
}
public class Person {
static {
System.out.println("我是静态代码块, 我执行了");
}
public Person(){
System.out.println("我是Person类的空参数构造方法");
}
public Person(int a){
System.out.println("我是Person类的带...........参数构造方法");
}
}
需求
使用静态代码块,初始化一些学生数据
实现步骤
示例代码
StudentDao类
public class StudentDao {
// 创建学生对象数组
private static Student[] stus = new Student[5];
static {
Student stu1 = new Student("heima001","张三","23","1999-11-11");
Student stu2 = new Student("heima002","李四","24","2000-11-11");
stus[0] = stu1;
stus[1] = stu2;
}
// 添加学生方法
public boolean addStudent(Student stu) {
// 2. 添加学生到数组
//2.1 定义变量index为-1,假设数组已经全部存满,没有null的元素
int index = -1;
//2.2 遍历数组取出每一个元素,判断是否是null
for (int i = 0; i < stus.length; i++) {
Student student = stus[i];
if(student == null){
index = i;
//2.3 如果为null,让index变量记录当前索引位置,并使用break结束循环遍历
break;
}
}
// 3. 返回是否添加成功的boolean类型状态
if(index == -1){
// 装满了
return false;
}else{
// 没有装满, 正常添加, 返回true
stus[index] = stu;
return true;
}
}
// 查看学生方法
public Student[] findAllStudent() {
return stus;
}
public void deleteStudentById(String delId) {
// 1. 查找id在容器中所在的索引位置
int index = getIndex(delId);
// 2. 将该索引位置,使用null元素进行覆盖
stus[index] = null;
}
public int getIndex(String id){
int index = -1;
for (int i = 0; i < stus.length; i++) {
Student stu = stus[i];
if(stu != null && stu.getId().equals(id)){
index = i;
break;
}
}
return index;
}
public void updateStudent(String updateId, Student newStu) {
// 1. 查找updateId, 在容器中的索引位置
int index = getIndex(updateId);
// 2. 将该索引位置, 使用新的学生对象替换
stus[index] = newStu;
}
}