- Student 类: 标准学生类,封装键盘录入的学生信息(id, name, age, birthday)
- StudentDao类: Dao(Data Access Object缩写)用于访问存储数据的数组或集合
- StudentService类 : 用来进行业务逻辑的处理(例如:判断录入id是否存在)
- StudentController类: 和用户打交道(接收用户需求,采集用户信息,打印数据到控制台)
分包思想
- 包:本质就是文件夹
- 创建包:(单级包、多级包)
继承的利弊
优点
- 提高代码复用性
- 提高代码维护性
- 让类与类之间产生了关系,是多态的前提
弊端 - 继承是侵入性
- 降低了代码的灵活性
继承关系,导致子类必须拥有父类非私有属性和方法,让子类自由的世界多了些约束
什么时候使用继承?
当类与类之间,存在相同共性的内容
并且产生is a的关系,就可以考虑使用继承,来优化代码。
好处:提高代码(复用性、维护性),是多态的前提
弊端:降低了代码灵活性,增强了代码的耦合性
继承的特点
- Java只支持单继承,不支持多继承,但支持多层继承
继承的成员变量访问特点
在子类方法中访问一个变量
- 子类局部范围找
- 子类成员范围找
- 父类成员范围找
** 注意:如果子父类中,出现了重名的成员变量,通过就近原则,会优先使用子类的,如果一定要使用父类的,可以通过super关键字,进行区分。
super关键字的用法和this关键字的用法相似 - this:代表本类对象的引用
- super:代表父类存储空间的标识(可以理解为父类对象引用)
继承
方法重写
-在继承体系中,子类出现了和父类中一模一样的方法声明
方法重写的应用场景
- 当子类需要父类的功能,而功能主体子类有自己特有内容,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
方法重载:在同一个类中,方法名相同,参数列表不同,与返回值无关。
方法重写:在继承体系中,子类出现了和父类一模一样的方法声明(方法名,参数列表,返回值类型)
重写注意事项 - 父类中私有方法不能被重写
- 父类静态方法,子类必须通过静态方法进行重写,父类非静态方法,子类也必须通过非静态方法进行重写
静态方法不能被重写,如果子类中,也存在一个方法声明一模一样的方法,可以理解为,子类父类中同名的方法,隐藏了起来,并非是方法重写
子类重写父类方法时,权限必须大于父类
接口
当一个类中的所有方法都是抽象方法的时候,我们就可以将其定义1为接口接口也是一种引用数据类型,它比抽象类还要抽象。
- 接口存在的两个重要意义
1、规则的定义
2、程序的扩展性 - 接口不能实例化
- 接口和类之间时实现关系,通过implements关键字表示
- 接口的子类(实现类)
要么重写接口中的所有抽象方法
要么是抽象类
JDK8版接口成员特点- JDK8版本后,java只对接口成员方法进行改进
- JDK8版本后
允许在接口中定义非抽象方法,但需要使用关键字default修饰,这些方法就是默认方法。
接口中默认方法的注意事项:
1、 默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字。
2、public可以省略,default不能省略
3、如果实现多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写。
JDK8后
1、允许在接口中定义非抽象方法,但需要使用关键字default修饰,这些方法就是默认方法
作用:解决接口升级问题
2、接口中允许定义static静态方法
接口中静态方法的注意事项: - 静态方法只能通过接口名调用,不能通过实现类或者对象名调用
- public可以省略,static不能省略
JDK9接口成员特点
接口使用思路
- 如果发现一个类中所有的方法都是抽象方法,那么就可以将该类,改进为一个接口
- 涉及到了接口大面积更新方法,而不想修改每一个实现类,就可以将更新的方法,定义为带有方法体的默认方法
- 希望默认方法调用的更加简洁,可以考虑设计static静态方法。(需要去掉default关键字)
- 默认方法中出现了重复代码,可以考虑抽取出一个私有方法。(需要去掉default关键字)
接口和类的关系 - 类和类的关系
继承关系,只能单继承,但可以多层继承 - 类和接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口 - 接口和接口关系
继承关系,可以单继承,也可以多继承
多态
同一个对象,在不同时刻表现出来的不同形态
多态的前提和体现
- 有继承/实现关系
- 方法重写
- 有父类引用指向子类对象
public class Test2Polymorpic {
/**
* 多态成员访问特点
* 成员变量: 编译看左边(父类),运行看左边(父类)
* 成员方法:编译看左边(父类),运行看右边(子类)
*/
public static void main(String[] args) {
Fu f = new Zi();
System.out.println(f.num);
f.method();
}
}
class Fu{
int num = 10;
public void method(){
System.out.println("Fu..method");
}
}
class Zi extends Fu{
int num = 20;
public void method(){
System.out.println("Zi..method");
}
}
为什么成员变量和成员方法的访问不一样呢
- 因为成员方法有重写,而成员变量没有
多态的好处和弊端
-多态的好处:提高了程序的扩展性
具体体现:定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的任意子类对象
- 多态的弊端:不能使用子类的特有功能
public class Test3Polymorpic {
public static void main(String[] args) {
useAnimal(new Cat());
useAnimal(new Dog());
}
public static void useAnimal(Animal animal) {
animal.eat();
}
}
abstract class Animal {
public abstract void eat();
}
class Dog extends Animal {
public void eat() {
System.out.println("狗吃肉");
}
}
class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
}
多态中的转型
- 向上转型
从子到父
父类引用指向子类对象 - 向下转型
从父到子
父类引用转为子类对象
public class Test4Polymorpic {
public static void main(String[] args) {
//向上转型,父类引用指向子类对象
Fu f = new Zi();
f.show();
//多态的弊端,不能调用子类特有的成员
//向下转型 父类类型,转换回子类类型
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");
}
}
多态中转型存在的风险
- 概述:如果被转的引用类型变量,对应的实际类型和目标类型不是同一类型,那么在转换的时候就会出现ClassCastException 类型转换异常
内部类
成员内部类
按照内部类在类中定义的位置不同,可以分为如下两种形式
- 在类的成员位置:成员内部类
- 在类的局部位置:局部内部类
public class Test1Inner {
public static void main(String[] args) {
/**
* 创建内部类对象的格式:
* 外部类名.内部类名 对象名 = new 外部类对象().new 内部类对象()
*/
Outer.Inner i = new Outer().new Inner();
i.show();
}
}
class Outer {
private int num = 10;
class Inner {
public void show() {
System.out.println("Inner...show");
System.out.println(num);
}
}
}
成员内部类
成员内部类,也属于(成员),既然是成员就可以被一些修饰符所修饰
私有成员内部类
public class Test2Innterclass {
/**
* 私有成员内部类演示
*/
public static void main(String[] args) {
//Outer.Inner oi = new Outer().new Inner();
Outer o = new Outer();
o.method();
}
}
class Outer{
private class Inner{
public void show(){
System.out.println("inner.show");
}
}
public void method(){
Inner i = new Inner();
i.show();
}
}
静态成员内部类
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");
}
}
}
局部内部类
局部内部类是在方法中定义的类,所以外界是无法直接使用,需要在方法内部创建对象并使用该类可以直接访问外部类中的成员,也可以访问方法内的局部变量
package com.innerclasstest.test4;
/**
* @Description TODO
* @Author Xm
* @Date 2022/8/6 14:54
*/
public class test4 {
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();
}
}
匿名内部类
概述:匿名内部类本质上是一个特殊的局部内部类(定义在方法内部)
前提:需要存在一个接口或类
public class Test5Innercalss {
/**
* 1.创建实现类,通过implements关键字去实现接口
* 2.重写方法
* 3.创建实现类对象
* 4.调用重写后的方法
* <p>
* 匿名内部类:
* 前提:需要存在类\接口
* 格式:
* 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();
//接口中存在多个方法
Inter1 i = new Inter1() {
@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 Inter1 {
void show1();
void show2();
}
class InterImpl implements Inter {
@Override
public void show() {
System.out.println("InterImpl 重写show方法");
}
}
匿名内部类在开发中的使用
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();
}
Lambda表达式
函数式编程思想
在数学中,函数就是输入量、输出量的一套计算方案,也就是”拿数据做操作“面向对象思想强调”必须通过对象的形式来做事“
函数式思想则尽量忽略面向对象的复杂语法:”强调做什么,而不是以什么形式去做“
组成Lambda表达式的三要素:形式参数,箭头,代码块
Lambda表达式格式
- 格式:(形式参数)->{代码块}
- 形式参数:如果多个参数,参数之间用逗号隔开,如果没有参数,留空即可
- ->:由英文中画线和大于符号组成,固定写法。代表指向动作
- 代码块:是我们具体要做的事情,也就是写的方法内容
Lambda使用前提 - 有一个接口
- 接口中有且仅有一个抽象方法
package com.lambda.test;
/**
* @Description TODO
* @Author Xm
* @Date 2022/8/7 10:05
*/
public class TestLambda {
/**
* Lambda表达式的使用前提
* 1、一个接口
* 2、接口中有且仅有一个抽象方法
*/
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();
}
含参的无返回值
public class LambdaTest2 {
public static void main(String[] args) {
useStringHandler(new StringHandler() {
@Override
public void printMessage(String msg) {
System.out.println("我是匿名内部类 "+msg);
}
});
//Lambda实现
useStringHandler((String msg)->{
System.out.println("我是lambda表达式 "+msg);
});
}
public static void useStringHandler(StringHandler stringHandler){
stringHandler.printMessage("summer");
}
}
interface StringHandler{
void printMessage(String msg);
}
无参有返回值的
import java.util.Random;
public class RandomNumHandlerDemo {
public static void main(String[] args) {
userRandomNumHandler(new RandomNumHandler() {
@Override
public int getNumber() {
Random r = new Random();
int num = r.nextInt(10) + 1;
return num;
}
});
userRandomNumHandler(() -> {
Random r = new Random();
int num = r.nextInt(10) + 1;
return num;
//注意:如果lambda所操作的接口中的方法,有返回值,一定要通过return语句,将结果返回
//否则回出现编译错误
});
}
public static void userRandomNumHandler(RandomNumHandler randomNumHandler) {
int result = randomNumHandler.getNumber();
System.out.println(result);
}
}
interface RandomNumHandler {
int getNumber();
}
带参带返回值的参数
package com.lambda.test4;
/**
* @Description TODO
* @Author Xm
* @Date 2022/8/7 10:23
*/
public class CalculatorDemo {
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);
}
省略规则:
- 参数类型可以省略,但是有多个参数的情况下,不能只省略一个
- 如果参数有且仅有一个,那么小括号可以省略
- 如果代码块的语句只有一条,可以省略大括号和分号,甚至是return
public class Test6 {
public static void main(String[] args) {
// useInter((double a, double b)->{
// return a+b;
//
// });
// useInter(( a, b)->{
// return a+b;
//
// });
useInter((a, b) ->
a + b
);
}
public static void useInter(Inter i) {
double result = i.method(12.3, 22.3);
System.out.println(result);
}
}
interface Inter {
double method(double a, double b);
}
Lambda表达式和匿名内部类的区别
所需类型不同
-匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
-lambda表达式:只能是接口
使用限制不同
- 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
- 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用lambda表表达式
实现原理不同 - 匿名内部类:编译之后,产生一个单独的.class字节码文件
- Lambda表达式:编译之后,没有单独的.class字节码文件,对应的字节码会在运行的时候动态生成