1. 继承
1.1 继承的实现(掌握)
-
继承的概念
继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法
-
实现继承的格式
继承通过extends实现 public class 子类名 extends 父类名{ }
-
格式:class 子类 extends 父类 { }
举例:class Dog extends Animal { }
- Fu:是父类,也被称为基类,超类
- Zi:是子类,也被称为派生类
继承带来的好处
继承可以让类与类之间产生关系,子父类关系,产生子父类后,子类则可以使用父类中非私有的成员。
- 提高了代码的复用性
- 提高了代码的维护性
- 让类与类之间产生了关系,是多态的前提
- 继承的弊端:
继承是侵入性的
降低了代码的灵活性
继承关系,导致子类必须拥有父类非私有属性和方法,让子类*的世界中多了些约束
增强了代码的耦合性
代码与代码之间存在关联都可以将其称之为“耦合”
- 耦合性:代码与代码之间存在关联都可以将其称之为“耦合”
什么时候使用继承?
当类与类之间,存在相同(共性)的内容,并且产生了ia a的关系,就可以考虑使用继承,来优化代码
继承的特点:
Java只支持单继承,不支持多继承,但支持多层继承
单继承:子类只能继承一个父类
多层继承:子类A继承父类B,父类B可以继承父类C
问题:为什么不支持多继承?
为了避免逻辑冲突问题,所以不支持多继承
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();
}
}
继承的特点代码
public class Granddad { public void drink() {
System.out.println("爷爷爱喝酒");
} } public class Father extends Granddad { public void smoke() {
System.out.println("爸爸爱抽烟");
} } public class Mother { public void dance() {
System.out.println("妈妈爱跳舞");
} }
public class Son extends Father {
// 此时,Son类中就同时拥有drink方法以及smoke方法
}
2. 继承中的成员访问特点
2.1 继承中变量的访问特点(掌握)
在子类方法中访问一个变量,采用的是就近原则。
子类局部范围找
子类成员范围找
父类成员范围找
如果都没有就报错(不考虑父亲的父亲…
注意:如果子父类中,出现了重名的成员变量,通过就近原则,会优先使用子类的,如果一定要使用父类的,可以通过super关键字,进行区分。
示例代码
public class Fu {
int a=10;
} public class Zi extends Fu{
//子父类当中,出现了重名的成员变量
int a=20;
public void method(){
int a=30;
System.out.println(a);//30 //需求1:在控制台打印本类成员变量20
System.out.println(this.a);//20
//需求2:在控制台打印父类成员变量10
System.out.println(super.a);//10
}
} public class Test {
public static void main(String[] args) {
Zi z=new Zi();
z.method();
}
}
结果打印 30 20 10
2.2 super(掌握)
- super关键字的用法和this关键字的用法相似
-
this&super关键字:
this:代表本类对象的引用
super:代表父类存储空间的标识(可以理解为父类对象引用)
-
this和super的使用分别
-
成员变量:
this.成员变量 - 访问本类成员变量
super.成员变量 - 访问父类成员变量
-
成员方法:
this.成员方法 - 访问本类成员方法
super.成员方法 - 访问父类成员方法
-
构造方法:
this(…) - 访问本类构造方法
super(…) - 访问父类构造方法
-
2.4 继承中成员方法的访问特点(掌握)
通过子类对象访问一个方法
子类成员范围找
父类成员范围找
如果都没有就报错(不考虑父亲的父亲…)
2.6 方法重写(掌握)
-
1、方法重写概念
在继承体系中,子类出现了和父类中一模一样的方法声明(方法名一样,参数列表也必须一样,返回值类型也是)
2、方法重写的应用场景
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
- 练习:手机类和新手机类
注意:
方法重写: 在继承体系中, 子类出现了和父类一模一样的方法声明 (方法名, 参数列表, 返回值类型)
方法重载: 在同一个类中, 方法名相同, 参数列表不同, 与返回值无关.-
3、Override注解
用来检测当前的方法,是否是重写的方法,起到【校验】的作用
public class iPearV1 {
/*
1. 定义手机类 iPearV1
call(String name) : 打电话方法
smallBlack() : 语音助手 (speak english...)
*/
public void call(String name){
System.out.println("给"+name+"打电话");
}
public void smallBlack(){
System.out.println("speak english...");
}
} public class iPearV2 extends iPearV1{
/* 2. 定义新手机类 iPearV2
call(String name) : 打电话方法
smallBlack() : 语音助手 (speak english... 说中文)
方法重写的应用场景:
当子类需要父类的功能,而功能主体子类有自己特有内容
可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
*/
@Override
public void smallBlack(){
super.smallBlack();//父类功能保留
System.out.println("说中文");
} } public class TestOverride {
/*
需求: 1. 定义手机类 iPearV1
call(String name) : 打电话方法
smallBlack() : 语音助手 (speak english...) 2. 定义新手机类 iPearV2
call(String name) : 打电话方法
smallBlack() : 语音助手 (speak english... 说中文) 方法重写的应用场景:
当子类需要父类的功能,而功能主体子类有自己特有内容
可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容 注意: 方法重写: 在继承体系中, 子类出现了和父类一模一样的方法声明 (方法名, 参数列表, 返回值类型) 方法重载: 在同一个类中, 方法名相同, 参数列表不同, 与返回值无关. */
public static void main(String[] args) {
iPearV2 i=new iPearV2();
i.smallBlack();
}
}
2.7 方法重写的注意事项(掌握)
方法重写的注意事项
私有方法不能被重写(父类私有成员子类是不能继承的)
子类重写父类方法时,访问权限必须大于等于父类 (public > protected> 默认 > 私有)
父类静态方法,子类必须通过静态方法进行重写,父类非静态方法,子类也必须通过非静态方法进行重写
注意:静态方法不能被重写!!!如果子类中,也存在一个方法声明一模一样的方法
可以理解为,子类将父类中同名的方法,隐藏了起来,并非是方法重写!现象是重写,本质上并不是
示例代码
public class Fu {
public static void show(){
System.out.println("Fu...");
}
void method(){
System.out.println("Fu...method");
}
} public class Zi extends Fu{
//@Override //注解:检查当前的方法是否是一个正确的重写方法
public static void show() {
System.out.println("Zi...");
} @Override
public void method() { }
} public class Test {
public static void main(String[] args) {
Zi z=new Zi();
z.show();
}
}
2.8 权限修饰符 (理解)
权限修饰符可以修饰的内容是成员 (成员变量,成员方法,构造方法)
2.3 继承中构造方法的访问特点(理解)
注意:子类中所有的构造方法默认都会访问父类中无参的构造方法
为什么?
子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据
所以,子类初始化之前,一定要先完成父类数据的初始化。
怎样初始化?
每一个子类构造方法的第一条语句默认都是:super()
注意:如果我们编写的类,没有手动指定父类,系统也会自动继承Object(Java继承体系中的最顶层父类)
问题:如果父类中没有空参构造方法,只有带参构造方法,会出现什么现象呢?
- 子类通过super,手动调用父类的带参构造方法
- 子类通过this去调用本类的其他构造方法,本类其他构造方法再通过super去手动调用父类的带参构造方法
注意:this(...)super(...)必须放在构造方法的第一行有效语句,并且二者不能共存
//如果一个类被public 修饰了类名必须和文件名一致
public class Test2 {
public static void main(String[] args) {
Zi z=new Zi(); }
}
class Fu{
int age;
//空参数构造方法
public Fu(){
System.out.println("父类空参构造方法");
}
//带参数构造方法
public Fu(int age){
this.age=age;
}
}
class Zi extends Fu{
public Zi(){
this(10); // this(),super()都必须放在构造方法第一条语句,二者不能共存
//super();
} public Zi(int age){
super(age); }
}
2.5 super内存图(理解)
对象在堆内存中,会单独存在一块super区域,用来存放父类的数据
总结:
子类当中所有的构造方法,默认都会通过super()访问父类中无参的构造方法
每一个子类构造方法的第一条语句默认都是:super()
this(...)super(...)必须放在构造方法的第一行有效语句,并且二者不能共存
黑马信息管理系统改进
思路:把学生类和老师类共性的内容向上抽取,抽取到一个Person父类,让学生类老师类继承Person类
步骤1:抽取Person类。
步骤2:优化StudentController类中,inputStudentInfo方法。将setXxx赋值方式,改进为构造方法初始化
注意:直接修改这种操作方式,不符合我们开发当中的一个原则
开闭原则(队扩展开放对修改关闭)
解决:重新创建一个OtherStudentController类
便写新的inputStudentInfo方法
步骤3:根据StudentController类,OtherStudentController类,向上抽取出BaseStudentController类
再让StudentController类,OtherStudentController类,继承BaseStudentController类
该三个部分
controller类
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 {
// 3. 将学生对象,传递给StudentService(业务员)中的addStudent方法
private StudentService studentService=new StudentService(); private Scanner sc=new Scanner(System.in);
//开启学生管理系统,并展示学生管理系统菜单
public void start() {
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();
//调用业务员中的deleteStudentById根据id,删除学生
studentService.deleteStudentById(delId);
//提示:删除成功
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\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() { 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); 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;
}
}
BaseTeacherController:
package com.itheima.edu.info.manager.controller; import com.itheima.edu.info.manager.domain.Teacher;
import com.itheima.edu.info.manager.service.TeacherService; import java.util.Scanner; public class BaseTeacherController {
private Scanner sc = new Scanner(System.in);
private TeacherService teacherService=new TeacherService();
public void start() {
teacherloop: while(true){ System.out.println("--------欢迎来到 <老师> 管理系统--------");
System.out.println("请输入您的选择: 1.添加老师 2.删除老师 3.修改老师 4.查看老师 5.退出");
String choice = sc.next(); switch (choice) {
case "1":
//System.out.println("添加");
addTeacher();
break;
case "2":
//System.out.println("删除");
deleteTeacherById();
break;
case "3":
//System.out.println("修改");
updateTeacher();
break;
case "4":
//System.out.println("查询");
findAllTeacher();
break;
case "5":
System.out.println("感谢您使用老师管理系统,再见!");
break teacherloop;
default:
System.out.println("您的输入有误,请重新输入");
break;
}
}
}
//修改老师
public void updateTeacher() {
String updateId = inputTeacherId();
Teacher newTeacher = inputTeacherInfo(updateId);
//调用业务员的修改方法
teacherService.updateTeacher(updateId,newTeacher);
System.out.println("修改成功");
} //删除老师
public void deleteTeacherById() {
String delId = inputTeacherId();
//调用业务员中的deleteStudentById根据i
//,删除老师
teacherService.deleteTeacherById(delId);
//提示:删除成功
System.out.println("删除成功");
} //查询老师
public void findAllTeacher() {
//1.从业务员中获取老师对象数组
Teacher[] teachers=teacherService.findAllTeacher();
//判断数组中是否有元素
if (teachers == null) {
System.out.println("查无信息,请添加后重试");
return;
} //3.遍历数组,获取学生信息,并打印在控制台
System.out.println("学号\t\t姓名\t年龄\t\t生日");
for (int i = 0; i < teachers.length; i++) {
Teacher t=teachers[i];
if (t != null) {
System.out.println(t.getId()+"\t"+t.getName()+"\t"+t.getAge()+"\t\t"+t.getBirthday());
}
}
} //添加老师
public void addTeacher() {
String id;
while (true){
//1.接受一个不存在的老师id
System.out.println("请输入老师id");
id=sc.next();
//2.判断id是否存在
boolean exists =teacherService.isExists(id);
if (exists) {
System.out.println("id已被占用,请重新输入");
}else{
break;
}
}
Teacher t = inputTeacherInfo(id); //5.将封装好的老师对象,传递给TeacherService继续完成添加操作
boolean result=teacherService.addTeacher(t);
if (result ) {
System.out.println("添加成功");
}else{
System.out.println("添加失败");
} }
//录入老师id
public String inputTeacherId(){
String id;
while(true){
//键盘接收要删除的老师id
System.out.println("请输入id");
id=sc.next();
boolean exists = teacherService.isExists(id);
if (!exists ) {
System.out.println("您输入的id不存在,请重新输入:");
}else{
break;
}
}
return id;
}
//录入老师信息,封装为老师对象类型
public Teacher inputTeacherInfo(String id){
return null;
}
}
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("请输入学生姓名");
String name=sc.next();
System.out.println("请输入学生年龄");
String age=sc.next();
System.out.println("请输入学生生日");
String birthday=sc.next();
//2.将学生信息封装为学生对象
Student stu=new Student(id,name,age,birthday); return stu;
}
}
OtherTeacherController:
package com.itheima.edu.info.manager.controller; import com.itheima.edu.info.manager.domain.Teacher;
import com.itheima.edu.info.manager.service.TeacherService; import java.util.Scanner; public class OtherTeacherController extends BaseTeacherController{
private Scanner sc = new Scanner(System.in);
//录入老师信息,封装为老师对象类型
public Teacher inputTeacherInfo(String id){
//3.接受老师的其他信息
System.out.println("请输入老师的姓名");
String name=sc.next();
System.out.println("请输入老师的年龄");
String age=sc.next();
System.out.println("请输入老师的生日");
String birthday=sc.next();
//封装为老师对象
Teacher t=new Teacher(id,name,age,birthday); return t;
}
}
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);
//键盘录入学生信息
//开闭原则:对扩展内容开放,对修改内容关闭
@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();
//2.将学生信息封装为学生对象
Student stu=new Student();
stu.setId(id);
stu.setName(name);
stu.setAge(age);
stu.setBirthday(birthday);
return stu;
}
}
TeacherController:
package com.itheima.edu.info.manager.controller; import com.itheima.edu.info.manager.domain.Student;
import com.itheima.edu.info.manager.domain.Teacher;
import com.itheima.edu.info.manager.service.TeacherService; import java.util.Scanner; public class TeacherController extends BaseTeacherController{
private Scanner sc = new Scanner(System.in);
//录入老师信息,封装为老师对象类型
public Teacher inputTeacherInfo(String id){
//3.接受老师的其他信息
System.out.println("请输入老师的姓名");
String name=sc.next();
System.out.println("请输入老师的年龄");
String age=sc.next();
System.out.println("请输入老师的生日");
String birthday=sc.next();
//封装为老师对象
Teacher t=new Teacher();
t.setId(id);
t.setName(name);
t.setAge(age);
t.setBirthday(birthday);
return t;
}
}
domain:
Person:
package com.itheima.edu.info.manager.domain; 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:
package com.itheima.edu.info.manager.domain; public class Student extends Person{
public Student() {
} public Student(String id, String name, String age, String birthday) {
super(id, name, age, birthday);
}
}
Teacher:
package com.itheima.edu.info.manager.domain; public class Teacher extends Person{
public Teacher() {
} public Teacher(String id, String name, String age, String birthday) {
super(id, name, age, birthday);
}
}
InfoManagerEntry
package com.itheima.edu.info.manager.entry; import com.itheima.edu.info.manager.controller.OtherStudentController;
import com.itheima.edu.info.manager.controller.OtherTeacherController;
import com.itheima.edu.info.manager.controller.StudentController;
import com.itheima.edu.info.manager.controller.TeacherController; import java.util.Scanner; public class InfoManagerEntry {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
//主菜单搭建
while (true){
System.out.println("--------欢迎来到黑马信息管理系统--------");
System.out.println("请输入您的选择: 1.学生管理 2.老师管理 3.退出");
String choice=sc.next();
switch (choice){
case "1":
//System.out.println("学生管理");
//开启学生管理系统
OtherStudentController studentController=new OtherStudentController();
studentController.start();
break;
case "2":
//System.out.println("老师管理");
OtherTeacherController teacherController=new OtherTeacherController();
teacherController.start();
break;
case "3":
System.out.println("感谢您的使用");
//退出当前正在运行的JVM虚拟机
System.exit(0);
break;
default:
System.out.println("您的输入有误,请重新输入");
break;
}
} } }
3.抽象类
3.1抽象类的概述(理解)
抽象方法:将共性的行为(方法)抽取到父类之后,发现该方法的实现逻辑无法在父类中给出具体明确,该方法就可以定义为抽象方法
抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类
抽象方法的定义格式:
public abstract 返回值类型 方法名(参数列表)
抽象类的定义格式:
public abstract class 类名{
}
3.3抽象类的案例(应用)
-
案例需求
定义猫类(Cat)和狗类(Dog)
猫类成员方法:eat(猫吃鱼)drink(喝水…)
狗类成员方法:eat(狗吃肉)drink(喝水…)
-
实现步骤
猫类和狗类中存在共性内容,应向上抽取出一个动物类(Animal)
父类Animal中,无法将 eat 方法具体实现描述清楚,所以定义为抽象方法
抽象方法需要存活在抽象类中,将Animal定义为抽象类
让 Cat 和 Dog 分别继承 Animal,重写eat方法
测试类中创建 Cat 和 Dog 对象,调用方法测试
代码实现
TestAnimal:
package com.itheima.test1; public class TestAnimal {
/*
需求:定义猫类(Cat)和狗类(Dog)
猫类成员方法:eat(猫吃鱼)drink(喝水…)
狗类成员方法:eat(狗吃肉)drink(喝水…) 步骤:
1. 猫类和狗类中存在共性内容,应向上抽取出一个动物类(Animal)
2. 父类Animal中,无法将 eat 方法具体实现描述清楚,所以定义为抽象方法
3. 抽象方法需要存活在抽象类中,将Animal定义为抽象类
4. 让 Cat 和 Dog 分别继承 Animal,重写eat方法
5. 测试类中创建 Cat 和 Dog 对象,调用方法测试
*/ public static void main(String[] args) {
Dog d=new Dog();
d.eat();
d.drink(); Cat c=new Cat();
c.eat();
c.drink();
}
}
Animal:
package com.itheima.test1; public abstract class Animal {
public void drink(){
System.out.println("喝水");
}
public abstract void eat();
}
Dog:
package com.itheima.test1; public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃肉");
}
}
Cat:
package com.itheima.test1; public class Cat extends Animal{ @Override
public void eat() {
System.out.println("猫吃鱼");
}
}
抽象类的注意事项
1.抽象类不能创建对象
2.抽象类中有构造方法
3.抽象类的子类
A:必须要重写父类中所有的抽象方法
B:可以将自己也变成一个抽象类
4.抽象类中的方法
抽象类中可以没有抽象方法,但是由抽象方法的类一定是抽象类
3.4模板设计模式
-
设计模式
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。是一套良好的编码风格,并非是一个技术点。
-
模板设计模式
把抽象类整体就可以看做成一个模板,模板中不能决定的东西定义成抽象方法 让使用模板的类(继承抽象类的类)去重写抽象方法实现需求
-
模板设计模式的优势
模板已经定义了通用结构,使用者只需要关心自己需要实现的功能即可
-
示例代码
模板类
CompositionTemplate类
package com.itheima.test2;
/*
* 作文模板类
* */
public abstract class CompositionTemplate {
public void write(){
System.out.println("<<我的爸爸>>");
body();
System.out.println("啊,这就是我的爸爸");
}
public abstract void body();
}
Test类
package com.itheima.test2; public class Test {
public static void main(String[] args) {
Tom t=new Tom();
t.write();
} }
Tom类
package com.itheima.test2; public class Tom extends CompositionTemplate{
@Override
public void body() {
System.out.println("那是一个秋天, 风儿那么缠绵,记忆中, " +
"那天爸爸骑车接我放学回家,我的脚卡在了自行车链当中, 爸爸蹬不动,他就站起来蹬..."); }
}
模板方法一般都是去定义骨架(结构),是不允许更改的
3.5final(应用)
-
fianl关键字的作用
final代表最终的意思,可以修饰成员方法,成员变量,类
-
final修饰类、方法、变量的效果
fianl修饰类:该类不能被继承(不能有子类,但是可以有父类)
final修饰方法:该方法不能被重写
-
final修饰变量:表明该变量是一个常量,不能再次赋值
变量是基本类型,不能改变的是值
变量是引用类型,不能改变的是地址值,但地址里面的内容是可以改变的
package com.itheima.mfinal; public class TestFinal {
/*
final修饰变量: 基本数据类型变量: 其值不能被更改 引用数据类型变量: 地址值不能被更改, 但是可以修改对象的属性值
*/
public static void main(String[] args) {
//常量的命名规范.如果是一个单词,所有字母大写,如果是多个单词,所有字母大写,但是中间是用下划线进行分隔
final int A=10;
//a=10;
final int MAX=10;
final int MAX_VALUE=20; final Student stu=new Student();
stu.setName("张三");
stu.setName("李四"); // stu=new Student();
}
}
/*
class Fu{ }
class Zi extends Fu{ }*/
class Student{
private String name;
//final修饰成员变量,初始化时机
//1.在创建的时候,直接给值
//2.在构造方法结束之前,完成赋值 final int a=10; public String getName() {
return name;
}
/* public Student(){
a=10;
}*/ public void setName(String name) {
this.name = name;
}
}
4.代码块
4.1代码块概述 (理解)
当程序启动完毕之后,程序就初始化一部分的学生数据
代码块:静态代码块
在Java中,使用 { } 括起来的代码被称为代码块
分类:
局部代码块
构造代码块
静态代码块
4.2代码块分类 (理解)
-
局部代码块
位置: 方法中定义
作用: 限定变量的生命周期,及早释放,提高内存利用率
示例代码
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("带参数构造方法>>>>>>");
}
}
静态代码块
位置: 类中方法外定义
特点: 需要通过static关键字修饰,随着类的加载而加载,并且只执行一次
作用: 在类加载的时候做一些数据初始化的操作
示例代码
package com.itheima.block.mstatic; public class Test {
/*
静态代码块:
位置:类中方法外定义
特点:需要通过static关键字修饰,随着类的加载而加载,并且只执行一次
作用:在类加载的时候做一些数据初始化的操作
*/
public static void main(String[] args) {
Person p1=new Person();
Person p2=new Person(10);
} }
class Person{
static {
System.out.println("我是静态代码块,我执行了");
}
public Person(){
System.out.println("我是Person类的空参数构造方法");
}
public Person(int a){
System.out.println("我是Person类的带参数构造方法>>>>");
}
}
我是静态代码块,我执行了
我是Person类的空参数构造方法
我是Person类的带参数构造方法>>>>
4.3黑马信息管理系统使用代码块改进 (应用)
需求:使用静态代码块,初始化一些学生数据
-
实现步骤
在StudentDao类中定义一个静态代码块,用来初始化一些学生数据
将初始化好的学生数据存储到学生数组中
示例代码
TeacherDao类
static{
Teacher teacher1=new Teacher("heima001","张三","23","1999-11-11");
Teacher teacher2=new Teacher("heima002","李四","24","2000-11-11");
teachers[0]=teacher1;
teachers[1]=teacher2;
}
StudentDao类
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;
}
1.接口
1.1黑马信息管理系统集合改进 (应用)
-
使用数组容器的弊端
容器长度是固定的,不能根据添加功能自动增长
没有提供用于赠删改查的方法
-
优化步骤
创建新的StudentDao类,OtherStudentDao
创建ArrayList集合容器对象
-
OtherStudentDao中的方法声明,需要跟StudentDao保持一致
注意:如果不一致,StudentService中的代码就需要进行修改
完善方法(添加、删除、修改、查看)
替换StudentService中的Dao对象
代码实现
package com.itheima.edu.info.manager.dao; import com.itheima.edu.info.manager.domain.Student; import java.util.ArrayList; public class OtherStudentDao extends BaseStudentDao{
// 集合容器
private static ArrayList<Student> stus=new ArrayList<>(); static{
Student stu1=new Student("heima001","张三","23","1999-11-11");
Student stu2=new Student("heima002","李四","24","2000-11-11");
stus.add(stu1);
stus.add(stu2);
} public boolean addStudent(Student stu) { // 添加学生方法 思路:将对象存入到数组中null元素对应的索引位置 stus.add(stu);
return true;
} public Student[] findAllStudent() {
Student[] students=new Student[stus.size()];
for (int i = 0; i < students.length; i++) {
students[i]=stus.get(i);
}
return students;
} public void deleteStudentById(String delId) {
//1.查找id在容器中的索引位置
int index=getIndex(delId);
//2.将该索引位置,使用null元素进行覆盖
stus.remove(index);
} public int getIndex(String id){
int index=-1;
for (int i = 0; i < stus.size(); i++) {
Student stu=stus.get(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);
stus.set(index,newStu);
}
}
StudentService类
public class StudentService {
// 创建StudentDao (库管)
private OtherStudentDao studentDao = new OtherStudentDao();
// 其他方法没有变化,此处省略...
}
1.2黑马信息管理系统抽取Dao (应用)
-
优化步骤
将方法向上抽取,抽取出一个父类 ( BaseStudentDao )
方法的功能实现在父类中无法给出具体明确,定义为抽象方法
让两个类分别继承 BaseStudentDao ,重写内部抽象方法
-
代码实现
BaseStudentDao类
public abstract class BaseStudentDao {
// 添加学生方法
public abstract boolean addStudent(Student stu);
// 查看学生方法
public abstract Student[] findAllStudent();
// 删除学生方法
public abstract void deleteStudentById(String delId);
// 根据id找索引方法
public abstract int getIndex(String id);
// 修改学生方法
public abstract void updateStudent(String updateId, Student newStu);
}
StudentDao类
public class StudentDao extends BaseStudentDao {
// 其他内容不变,此处省略
}
OtherStudentDao类
public class OtherStudentDao extends BaseStudentDao {
// 其他内容不变,此处省略
}
1.3接口的概述(理解)
场景:一个类中,所有的方法都是抽象方法。 制定规则
当一个类中的所有方法都是抽象方法的时候,我们就可以将其定义为接口
- 接口也是也是一种引用数据类型,它比抽象类还要抽象
-
Java中接口存在的两个重要意义
规则的定义
程序的扩展性
1.4接口的定义和特点(记忆)
接口用关键字interface修饰
- public interface 接口名{}
- 接口不能实例化
- 接口和类之间是实现关系,通过implements关键字表示
- public class 类名 implements 接口名{}
- 接口的子类(实现类)
要么重写接口中的所有抽象方法
要么是抽象类
注意:接口和类的实现关系,可以单实现,也可以多实现
public class 类名 implements 接口名1,接口名2{}
Inter接口类
package com.itheima.test1; public interface
Inter {
public abstract void study();
}
InterA接口类
package com.itheima.test1; public interface InterA {
public abstract void print1();
public abstract void print2();
public abstract void study();
}
InterImpl实现类
package com.itheima.test1; public class InterImpl implements Inter,InterA{
@Override
public void study() {
System.out.println("我是实现类中的study方法");
} @Override
public void print1() { } @Override
public void print2() { }
}
Test1Interface测试类
package com.itheima.test1; public class Test1Interface {
/*
接口的定义格式:
public interface 接口名 {} 类实现接口的格式:
public class 类名 implements 接口名 {} */
public static void main(String[] args) {
//Inter i=new Inter()
InterImpl ii=new InterImpl();
ii.study();
}
}
1.5接口的成员特点(记忆)
-
成员特点
-
成员变量
只能是常量 默认修饰符:public static final
-
构造方法
没有,因为接口主要是扩展功能的,而没有具体存在
-
成员方法
只能是抽象方法
默认修饰符:public abstract
关于接口中的方法,JDK8和JDK9中有一些新特性,后面再讲解
-
代码演示
接口
public interface Inter {
public static final int NUM = 10; public abstract void show();
}
实现类
class InterImpl implements Inter{ public void method(){
// NUM = 20;
System.out.println(NUM);
} public void show(){ }
}
测试类
public class TestInterface {
/*
成员变量: 只能是常量 系统会默认加入三个关键字
public static final
构造方法: 没有
成员方法: 只能是抽象方法, 系统会默认加入两个关键字
public abstract
*/
public static void main(String[] args) {
System.out.println(Inter.NUM);
} }
JDK8版本后,Java只对接口的成员方法就行了改进
允许接口定义带有方法体的方法(非抽象方法),但是需要使用关键字default修饰,这些方法就是默认方法。作用:解决接口升级问题
接口中允许static静态方法
接口中默认方法的定义格式:
格式:public default 返回值类型 方法名(参数列表){ }
举例:public default void show(){ }
接口中默认方法的注意事项:
- 默认方法不是抽象方法,所以不强制被重写,但是可以被重写,重写的时候去掉default关键字
- public可以省略,default不能省略
- 如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须队该方法进行重写
接口中静态方法的定义格式
格式:public static 返回值类型 方法名(参数列表){ }
范例:public static void show(){ }
接口中静态方法的注意事项:
静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
public可以省略,static不能省略
JDK9版本接口成员的特点
接口中私有方法的定义格式:
格式1:private 返回值类型 方法名(参数列表){ }
范例1:private void show(){ }
格式2:private static 返回值类型 方法名(参数列表){ }
范例2:private static void method(){ }
接口的使用思路
如果发现一个类中所有的方法都是抽象方法,那么就可以将该类,改进为一个接口
涉及到了接口大面积更新方法,而不想去修改每一个实现类,就可以将更新的方法,定义为带有方法体的默认方法
希望默认方法的调用更加简洁,可以考虑设计为static静态方法(需要去掉default关键字)静态方法只能通过接口名进行调用
默认方法中出现了重复的代码,可以考虑抽取出一个私有方法(需要去掉default关键字)
1.6类和接口的关系(记忆)
-
类与类的关系
继承关系,只能单继承,但是可以多层继承
-
类与接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
-
接口与接口的关系
继承关系,可以单继承,也可以多继承
InterA
public interface InterA {
public abstract void showA(); public default void method(){
System.out.println("InterA...method方法");
}
}
InterB
public interface InterB {
public abstract void showB(); public default void method(){
System.out.println("InterB...method方法");
}
}
InterC
public interface InterC extends InterA,InterB{
@Override
public default void method() {
System.out.println("InterC接口,解决代码逻辑冲突问题, 重写method方法");
}
}
InterImpl
public class InterImpl implements InterC{ @Override
public void showA() { } @Override
public void showB() { }
}
TestInterface
public class TestInterface {
public static void main(String[] args) {
InterImpl ii=new InterImpl();
ii.method();
}
}
1.7黑马信息管理系统使用接口改进 (应用)
-
实现步骤
将 BaseStudentDao 改进为一个接口
让 StudentDao 和 OtherStudentDao 去实现这个接口
-
代码实现
BaseStudentDao接口
public interface BaseStudentDao {
// 添加学生方法
public abstract boolean addStudent(Student stu);
// 查看学生方法
public abstract Student[] findAllStudent();
// 删除学生方法
public abstract void deleteStudentById(String delId);
// 根据id找索引方法
public abstract int getIndex(String id);
// 修改学生方法
public abstract void updateStudent(String updateId, Student newStu);
}
StudentDao类
public class StudentDao implements BaseStudentDao {
// 其他内容不变,此处省略
}
OtherStudentDao类
public class OtherStudentDao implements BaseStudentDao {
// 其他内容不变,此处省略
}
1.8黑马信息管理系统解耦合改进 (应用)
-
实现步骤
创建factory包,创建 StudentDaoFactory(工厂类)
提供 static 修改的 getStudentDao 方法,该方法用于创建StudentDao对象并返回
代码实现
代码实现
StudentDaoFactory类
public class StudentDaoFactory {
public static OtherStudentDao getStudentDao(){
return new OtherStudentDao();
}
}
StudentService类
public class StudentService {
// 创建StudentDao (库管)
// private OtherStudentDao studentDao = new OtherStudentDao(); // 通过学生库管工厂类, 获取库管对象
private OtherStudentDao studentDao = StudentDaoFactory.getStudentDao();
}
3.多态
3.1多态的概述(记忆)
-
什么是多态
同一个对象,在不同时刻表现出来的不同形态
- 举例:猫
- 我们可以说是猫:猫 cat=new 猫(); 我们也可以说猫是动物:动物animal=new 猫();这里猫在不同的时刻表现出来了不同的形态,这就是多态
-
多态的前提
要有继承或实现关系
要有方法的重写
要有父类引用指向子类对象
代码演示
package com.itheima.test1; public class Test1Polymorphic {
/*
多态的前提
1.要有继承或实现关系
2.要有方法的重写
3.要有父类引用指向子类对象
*/
public static void main(String[] args) {
//当前事物,是一只动物
Animal a=new Cat();
a.eat();
//当前事物,是一只猫
Cat c=new Cat();
}
}
class Animal{
public void eat(){
System.out.println("动物吃饭");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
3.2多态中的成员访问特点(记忆)
-
成员访问特点
- 构造方法:同继承一样,子类会通过super访问父类构造方法
- 成员变量:编译看左边(父类),执行看左边(父类)
- 成员方法:编译看左边(父类),执行看右边(子类)
-
代码演示
package com.itheima.test2; public class Test2Polymorpic {
/*
多态的成员访问特点: 成员变量: 编译看左边 (父类), 运行看左边 (父类) 成员方法: 编译看左边 (父类), 运行看右边 (子类)
*/
public static void main(String[] args) {
//多态的形式创建对象
Fu fu=new Zi();
System.out.println(fu.num);
fu.method();
}
} class Fu{
int num=10;
public void method(){
System.out.println("Fu...method");
}
}
class Zi extends Fu{
@Override
public void method() {
int num=20;
System.out.println("Zi...method");
}
}
为什么成员变量和成员方法的访问不一样呢?
因为成员方法有重写,而成员变量没有
3.3多态的好处和弊端(记忆)
-
好处
提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,该方法就可以接收这父类的任意子类对象
-
弊端
不能使用子类的特有功能
package com.itheima.test3; public class Test3Polymorpic {
public static void main(String[] args) {
useAnimal(new Dog());
useAnimal(new Cat());
}
public static void useAnimal(Animal a){ //Animal a=new Dog();
a.eat(); //Animal a=new Cat();
//a.watchHome();
} }
abstract class Animal{
public abstract void eat();
} class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃肉");
}
public void watchHome(){
System.out.println("看家");
}
} class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
3.4多态中的转型(应用)
-
向上转型
父类引用指向子类对象就是向上转型 从子到父
-
向下转型 从父到子 父亲引用转为子类对象
格式:子类型 对象名 = (子类型)父类引用;
代码演示
package com.itheima.test4; public class Test4Polymorpic {
public static void main(String[] args) {
//1.向上转型:父类引用指向子类对象
Fu f=new Zi();
f.show();
//多态的弊端,不能调用子类特有的成员
//f.method; //A:直接创建子类对象
//B:向下转型:从父类类型转换为子类类型
Zi z=(Zi)f;//强制转换
z.method();
}
} class Fu{
public void show(){
System.out.println("Fu..show...");
}
}
class Zi extends Fu{
@Override
public void show() {
System.out.println("Zi..show...");
}
public void method(){
System.out.println("我是子类特有的方法,method");
}
}
3.5多态中转型存在的风险和解决方案 (应用)
-
风险
如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException
-
解决方案
-
关键字
instanceof
-
使用格式
变量名 instanceof 类型
通俗的理解:判断关键字左边的变量,是否是右边的类型,返回boolean类型结果
-
代码演示
package com.itheima.test3; public class Test3Polymorpic {
public static void main(String[] args) {
useAnimal(new Dog());
useAnimal(new Cat());
}
public static void useAnimal(Animal a){ //Animal a=new Dog();
a.eat(); //Animal a=new Cat();
//a.watchHome();
if(a instanceof Dog){
Dog dog=(Dog) a;
dog.watchHome();//ClassCastException 类型转换异常
} // Dog dog=(Dog) a;
// dog.watchHome();//ClassCastException 类型转换异常
} }
abstract class Animal{
public abstract void eat();
} class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃肉");
}
public void watchHome(){
System.out.println("看家");
}
} class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
3.6黑马信息管理系统多态改进 (应用)
-
实现步骤
StudentDaoFactory类中方法的返回值定义成父类类型BaseStudentDao
StudentService中接收方法返回值的类型定义成父类类型BaseStudentDao
代码实现
StudentDaoFactory类
public class StudentDaoFactory {
public static BaseStudentDao getStudentDao(){
return new OtherStudentDao();
}
}
StudentService类
public class StudentService {
// 创建StudentDao (库管)
// private OtherStudentDao studentDao = new OtherStudentDao(); // 通过学生库管工厂类, 获取库管对象
private BaseStudentDao studentDao = StudentDaoFactory.getStudentDao();
}
4.内部类
4.1 内部类的基本使用(理解)
-
内部类概念
在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类
-
内部类定义格式
-
格式&举例:
/*
格式:
class 外部类名{
修饰符 class 内部类名{ }
}
*/ class Outer {
public class Inner { }
}内部类的访问特点
内部类可以直接访问外部类的成员,包括私有
外部类要访问内部类的成员,必须创建对象
-
按照内部类在类中定义的位置不同,可以分为如下两种形式
在类的成员位置:成员内部类
在类的局部位置:局部内部类
成员内部类,外界如何创建对象使用呢?
格式:
外部类名.内部类名 对象名=new 外部类对象().new 内部类对象();
package com.itheima.test1; public class TestInner {
public static void main(String[] args) { /*
* 创建内部类对象的格式
* 外部类名.内部类名 对象名=new 外部类对象().new 内部类对象();
* */
Outher.Inner i=new Outher().new Inner();
System.out.println(i.num);
i.show();
}
}
class Outher{
private int a=10;
class Inner{
int num=10; public void show(){
System.out.println("Inner..show");
//内部类访问外部类成员,可以直接访问,包括私有
System.out.println(a);
}
}
}
2.2 成员内部类(理解)
成员内部类,也属于(成员),既然是成员就可以被一些修饰符所修饰
private
私有成员内部类访问:在自己所在的外部类中创建对象访问。
static
静态成员内部类访问格式:外部类名.内部类名 对象名=new 外部类名.内部类名();
静态成员内部类中的静态方法:外部类名.内部类名.方法名();
私有的成员内部类:
package com.itheima.test2;
public class Test2Innerclass {
//私有的成员内部类
public static void main(String[] args) {
//Outer.Inner oi=new Outer().new Inner();
Outer outer=new Outer();
outer.method();
}
}
class Outer{
private class Inner{
public void show(){
System.out.println("inner...show");
}
}
public void method(){
Inner i=new Inner();
i.show();
}
}
静态的成员内部类
package com.itheima.test3; public class Test3Innerclass {
//静态的成员内部类
public static void main(String[] args) {
//外部类名.内部类名 对象名=new 外部类名.内部类名();
Outer.Inner oi=new Outer.Inner();
oi.show();
Outer.Inner.method();
}
}
class Outer{
static class Inner{
public void show(){
System.out.println("inner...show");
}
public static void method(){
System.out.println("inner...method");
}
} }
2.3 局部内部类(理解) 平时很少编写
-
局部内部类定义位置
局部内部类是在方法中定义的类
-
局部内部类方式方式
局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用
该类可以直接访问外部类的成员,也可以访问方法内的局部变量
示例代码
package com.itheima.test4; public class Test4Innerclass {
/*
* 局部内部类
* 访问方式:之恩那个在方法中,创建对象并访问
* */
public static void main(String[] args) {
Outer o= new Outer();
o.method();
}
}
class Outer{
int a=10;
public void method(){
int b=20;
class Inner{
public void show(){
System.out.println("show...");
System.out.println(a);
System.out.println(b);
}
} Inner i=new Inner();
i.show();
}
}
2.4 匿名内部类(应用)
概述:匿名内部类本质上是一个特殊的局部内部类(定义在方法内部)
-
匿名内部类的前提
存在一个类或者接口,这里的类可以是具体类也可以是抽象类
-
匿名内部类的格式
格式:new 类名 ( ) { 重写方法 } new 接口名 ( ) { 重写方法 }
- 理解:匿名内部类是将(继承/实现)(方法重写)(创建对象)三个步骤,放在了一步进行
- 匿名内部类的本质
- 本质:是一个继承了该类或者实现了该接口的子类匿名对象
- 匿名内部类的细节
- 匿名内部类可以通过多态的形式接受
举例:
package com.itheima.test5; public class Test5Innerclass {
/*
* 1.创建实现类,通过implements关键字去实现接口
* 2.重写方法
* 3.创建类对象
* 4.调用重写后的方法
*
* 匿名内部类:需要存在类/接口
* 格式:
* new 类名/接口名(){
* 重写方法
* }
* */
public static void main(String[] args) {
InterImpl ii=new InterImpl();
ii.show(); //匿名内部类的理解:将继承/实现,方法重写,创建对象,放在了一部进行.
//实现了Inter 接口的,一个实现类对象
new Inter(){ @Override
public void show() {
System.out.println("我是匿名内部类中的show方法");
}
}.show(); //接口中存在多个方法
Inter2 i=new Inter2(){ @Override
public void show1() {
System.out.println("show1");
} @Override
public void show2() {
System.out.println("show2");
}
};
i.show1();
i.show2(); }
}
interface Inter{
void show();
}
interface Inter2{
void show1();
void show2();
}
class InterImpl implements Inter{ @Override
public void show() {
System.out.println("InterImpl 重写的show方法");
}
}
2.4 匿名内部类在开发中的使用(应用)
-
匿名内部类在开发中的使用
当方法的形式参数是接口或者抽象类时,可以将匿名内部类作为实际参数进行传递
-
示例代码:
package com.itheima.test6; public class TestSwimming {
public static void main(String[] args) { goSwimming(new Swimming() {
@Override
public void swim() {
System.out.println("我们去游泳吧");
}
});
} public static void goSwimming(Swimming swimming){
swimming.swim();
}
}
/*
* 使用接口方法
*
* */ /*
* 游泳接口
* */
interface Swimming{
void swim();
}
5.Lambda表达式
5.1体验Lambda表达式【理解】
代码更少,关注点更明确了
1.方法要一个接口,我得给个实现类对象
2.创建匿名内部类对象,重写方法
3.方法要干嘛呢,打印一句话吧
面向对象思想:更多关注怎么做,谁来(哪个对象去做)
更多关注做什么,函数式编程思想
在数学中,函数就是有输入量,输出量的一套计算方案,也就是“拿数据做计算”
面向对象思想强调“必须通过对象的形式来做事情”
函数式思想则尽量忽略面向对象的复杂算法:“强调做什么,而不是以什么形式去做”
而我们要学习的Lambda表达式就是函数式编程思想的体现
5.2Lambda表达式的标准格式【理解】
-
格式:
(形式参数) -> {代码块}
形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
->:由英文中画线和大于符号组成,固定写法。代表指向动作
代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
-
组成Lambda表达式的三要素:
形式参数,箭头,代码块
Lambda表达式的使用前提
有一个接口
接口中有且仅有一个抽象方法
练习1:
编写一个接口(ShowHandler)
在该接口中存在一个抽象方法(show),该方法是无参数无返回值
在测试类(ShowHandlerDemo)中存在一个方法(useShowHandler),方法的参数是ShowHandler类型的,在方法的内部调用了ShowHandler的show方法
package com.itheima.test2; public class TestLambda {
/*
Lambda表达式的使用前提
1. 一个接口
2. 接口中有且仅有一个抽象方法 练习1:
1. 编写一个接口(ShowHandler)
2. 在该接口中存在一个抽象方法(show),该方法是无参数无返回值
3. 在测试类(ShowHandlerDemo)中存在一个方法(useShowHandler)
方法的的参数是ShowHandler类型的
在方法内部调用了ShowHandler的show方法
*/
public static void main(String[] args) {
useShowHandler(new ShowHandler() {
@Override
public void show() {
System.out.println("我是匿名内部类当中的show方法");
}
}); //Lambda实现
useShowHandler(()->{
System.out.println("我是Lambda当中的show方法");
});
} public static void useShowHandler(ShowHandler showHandler){
showHandler.show();
}
}
interface ShowHandler{
void show();
}
Lambda表达式的练习2
1.首先存在一个接口(StringHandler)
2.在该接口中存在一个抽象方法(PrintMessage),该方法是有参数无返回值
3.在测试类(StringHandlerDemo)中存在一个方法(useStringHandler),方法的参数是StringHandler类型的,在方法的内部调用了StringHandler的printMessage方法
package com.itheima.test3; public class StringHandlerDemo {
/*
1.首先存在一个接口(StringHandler)
2.在该接口中存在一个抽象方法(printMessage),该方法是有参数无返回值
3.在测试类(StringHandlerDemo)中存在一个方法(useStringHandler)
方法的的参数是StringHandler类型的
在方法内部调用了StringHandler的printMessage方法
*/
public static void main(String[] args) {
useStringHandler(new StringHandler() {
@Override
public void printMessage(String msg) {
System.out.println("我是匿名内部类"+msg);
}
}); useStringHandler((String msg)->{
System.out.println("我是Lambda表达式 "+msg);
});
}
public static void useStringHandler(StringHandler stringHandler){
stringHandler.printMessage("itheima");
}
}
interface StringHandler{
void printMessage(String msg);
}
Lambda表达式练习3
1. 首先存在一个接口(RandomNumHandler)
2. 在该接口中存在一个抽象方法(getNumber),该方法是无参数但是有返回值
3. 在测试类(RandomNumHandlerDemo)中存在一个方法(useRandomNumHandler)
方法的的参数是RandomNumHandler类型的
在方法内部调用了RandomNumHandler的getNumber方法
package com.itheima.test4; import java.util.Random; public class RandomNumHandlerDemo {
/* 1. 首先存在一个接口(RandomNumHandler)
2. 在该接口中存在一个抽象方法(getNumber),该方法是无参数但是有返回值
3. 在测试类(RandomNumHandlerDemo)中存在一个方法(useRandomNumHandler)
方法的的参数是RandomNumHandler类型的
在方法内部调用了RandomNumHandler的getNumber方法*/
public static void main(String[] args) {
useRandomNumHandler(new RandomNumHandler() {
@Override
public int getNumber() {
Random r=new Random();
int num=r.nextInt(10)+1;
return num;
}
}); useRandomNumHandler(()->{
Random r=new Random();
int num=r.nextInt(10)+1;
return num;
//注意:如果Lambda所操作的接口中的方法,有返回值,一定要通过return语句,将结果返回,否则会出现编译错误
}); }
public static void useRandomNumHandler(RandomNumHandler randomNumHandler){
int result=randomNumHandler.getNumber();
System.out.println(result);
} }
interface RandomNumHandler{
int getNumber();
}
Lambda表达式的练习4
1. 首先存在一个接口(Calculator)
2. 在该接口中存在一个抽象方法(calc),该方法是有参数也有返回值
3. 在测试类(CalculatorDemo)中存在一个方法(useCalculator)
方法的的参数是Calculator类型的
在方法内部调用了Calculator的calc方法
package com.itheima.test5; import com.sun.source.tree.BreakTree; public class CalculatorDemo {
/* 1. 首先存在一个接口(Calculator)
2. 在该接口中存在一个抽象方法(calc),该方法是有参数也有返回值
3. 在测试类(CalculatorDemo)中存在一个方法(useCalculator)
方法的的参数是Calculator类型的
在方法内部调用了Calculator的calc方法
*/
public static void main(String[] args) {
useCalculator(new Calculator() {
@Override
public int calc(int a, int b) {
return a+b;
}
}); useCalculator((int a,int b)->{
return a+b;
});
}
public static void useCalculator(Calculator calculator){
int result=calculator.calc(10,20);
System.out.println(result);
}
}
interface Calculator{
int calc(int a,int b);
}
5.6Lambda表达式的省略模式【应用】
省略的规则
参数类型可以省略。但是有多个参数的情况下,不能只省略一个
如果参数有且仅有一个,那么小括号可以省略
如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
package com.itheima.Test6; public class Test6 {
public static void main(String[] args) {
useInter((a,b) ->
a + b
);
} public static void useInter(Inter i) {
double a = i.method(12.3, 22.3);
System.out.println(a);
}
} interface Inter {
//用于计算a+b的结果并返回
double method(double a, double b);
}
5.8Lambda表达式和匿名内部类的区别【理解】
-
所需类型不同
匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
Lambda表达式:只能是接口
-
使用限制不同
如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
-
实现原理不同
匿名内部类:编译之后,产生一个单独的.class字节码文件
Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成