视频地址:https://www.bilibili.com/video/BV1Cv411372m
此笔记是 P105 - P112
1.包(理解)
-
什么是包?
- 包是用来分门别类的管理各种不同类的,类似于文件夹、建包利于程序的管理和维护。
- 建包的语法格式:package 公司域名倒写.技术名称。报名建议全部英文小写,且具备意义
package com.even.javabean; public class Student { }
- 建包语句必须在第一行,一般IDEA工具会帮助创建
-
导包
- 相同包下的类可以直接访问,不同包下的类必须导包,才可以使用!导包格式:import 包名.类名;
- 假如一个类中需要用到不同类,而这个两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问。
2.权限修饰符(掌握)
-
什么是权限修饰符?
- 权限修饰符:是用来控制一个成员能够被访问的范围的。
- 可以修饰成员变量,方法,构造器,内部类,不同权限修饰符修饰的成员能够被访问的范围将受到限制。
-
权限修饰符的分类和具体作用范围:
- 权限修饰符:有四种作用范围由小到大(private -> 缺省 -> protected - > public )
- 学完权限修饰符需要具备如下能力
- 能够识别别人定义的成员的访问权限。
- 自己定义成员(方法,成员变量,构造器等)一般满足如下要求:
- 成员变量一般私有。
- 方法一般公开。
- 如果该成员只希望本类访问,使用private修饰。
- 如果该成员只希望本类,同一个包下的其他类和子类访问,使用protected修饰。
3.final(面试)
-
final的作用
- final 关键字是最终的意思,可以修饰(方法,变量,类)
- 修饰方法:表明该方法是最终方法,不能被重写。
- 修饰变量:表示该变量第一次赋值后,不能再次被赋值(有且仅能被赋值一次)。
- 修饰类:表明该类是最终类,不能被继承。
- 开发中一般情况下是不会加final的,只有在特殊情况下。
- final基本上不会用来修饰实例成员变量,没有意义!
- 因为它是属于对象的,用final修饰后,所有对象都是初始赋于的值,不能改变。
-
final修饰变量的注意
- final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。
- final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生变化的。
4.常量(掌握)
4.1 常量概述和基本作用
-
常量
- 常量是使用了 public static final 修饰的成员变量,必须有初始化值,而且执行的过程中其值不能被改变。
- 常量的作用和好处:可以用于做系统的配置信息,方便程序的维护,同时也能提高可读性。
- 常量命名规范:英文单词全部大写,多个单词下划线连接起来。
public class Constant { public static final String SCHOOL_NAME = “传智教育"; public static final String LOGIN_NAME = “admin"; public static final String PASS_WORD = “123456"; }
-
常量的执行原理
- 在编译阶段会进行“宏替换”,把使用常量的地方全部替换成真实的字面量。
- 这样做的好处是让使用常量的程序的执行性能与直接使用字面量是一样的。
4.2 常量做信息标志和分类
-
案例说明:
- 现在开发的超级玛丽游戏需要接收用户输入的四个方向的信号(上下左右),以便控制玛丽移动的方向。
-
选择常量做信息标志和分类:
- 代码可读性好,实现了软编码形式。
import javax.swing.*; import java.awt.event.ActionEvent; /** 目标: 常量的其他作用,做信息标志和信息分类(其实也是一种配置形式) */ public class ConstantDemo2 { public static final int UP = 1; // 上 public static final int DOWN = 2; // 上 public static final int LEFT = 3; // 左 public static final int RIGHT = 4; // 右 public static void main(String[] args) { // 1、创建一个窗口对象(桌子) JFrame win = new JFrame(); // 2、创建一个面板对象(桌布) JPanel panel = new JPanel(); // 3、把桌布垫在桌子上 win.add(panel); // 4、创建四个按钮对象 JButton btn1 = new JButton("上"); JButton btn2 = new JButton("下"); JButton btn3 = new JButton("左"); JButton btn4 = new JButton("右"); // 5、把按钮对象添加到桌布上去 panel.add(btn1); panel.add(btn2); panel.add(btn3); panel.add(btn4); // 6、显示窗口 win.setLocationRelativeTo(null); win.setSize(300,400); win.setVisible(true); btn1.addActionListener(new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { move(UP) ; // 让玛丽往上跳 } }); btn2.addActionListener(new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { move(ConstantDemo2.DOWN) ; // 让玛丽往下跳 } }); btn3.addActionListener(new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { move(LEFT) ; // 让玛丽往左跑 } }); btn4.addActionListener(new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { move(RIGHT) ; // 让玛丽往右跑 } }); } public static void move(int flag){ // 控制玛丽移动 switch (flag) { case UP: System.out.println("玛丽往上飞了一下~~"); break; case DOWN: System.out.println("玛丽往下蹲一下~~"); break; case LEFT: System.out.println("玛丽往左跑~~"); break; case RIGHT: System.out.println("玛丽往→跑~~"); break; } } }
5.枚举(重要)
5.1 枚举的概述
-
枚举的概述
- 枚举是Java中的一种特殊类型。
- 枚举的作用:“是为了做信息的标志和信息的分类”。
-
定义枚举类的格式:
修饰符 enum 枚举名称{ 第一行都是罗列枚举类实例的名称。[即第1行都是罗列对象的名称] }
enum Season{ SPRING , SUMMER , AUTUMN , WINTER; }
-
反编译后观察枚举的特征:
public enum Season{ // 枚举的第一行必须罗列枚举类的对象名称,建议全部大写。 SPRING , SUMMER , AUTUMN , WINTER; //这4个就是当前枚举类的对象名称 }
// 以下就是反编译后的代码 Compiled from "Season.java" public final class Season extends java.lang.Enum<Season> { public static final Season SPRING = new Season(); public static final Season SUMMER = new Season(); public static final Season AUTUMN = new Season(); public static final Season WINTER = new Season(); public static Season[] values(); public static Season valueOf(java.lang.String); }
-
枚举的特征:
- 枚举类都是继承了枚举类型:java.lang.Enum
- 枚举都是最终类(final 修饰),不可以被继承。
- 构造器都是私有的,枚举对外不能创建对象。
- 枚举类的第一行默认都是罗列枚举对象的名称的。
- 上例来说,就是罗列了当前类的4个对象,而且只能有这4个,因为构造器私有了。
- 枚举类相当于是多例模式。
5.2 枚举的使用场景演示
-
案例说明:
-
现在开发的超级玛丽游戏需要接收用户输入的四个方向的信号(上下左右),以便控制玛丽移动的方向。
-
选择常量做信息标志和分类:
-
虽然可以实现可读性,但是入参值不受约束,代码相对不够严谨。
-
枚举做信息标志和分类:
- 代码可读性好,入参约束严谨,代码优雅,是最好的信息分类技术!建议使用!
/** 做信息标志和分类 */ public enum Orientation { UP, DOWN, LEFT, RIGHT; }
import javax.swing.*; import java.awt.event.ActionEvent; /** 目标: 常量的其他作用,做信息标志和信息分类(其实也是一种配置形式) */ public class EnumDemo2 { public static void main(String[] args) { // 1、创建一个窗口对象(桌子) JFrame win = new JFrame(); // 2、创建一个面板对象(桌布) JPanel panel = new JPanel(); // 3、把桌布垫在桌子上 win.add(panel); // 4、创建四个按钮对象 JButton btn1 = new JButton("上"); JButton btn2 = new JButton("下"); JButton btn3 = new JButton("左"); JButton btn4 = new JButton("右"); // 5、把按钮对象添加到桌布上去 panel.add(btn1); panel.add(btn2); panel.add(btn3); panel.add(btn4); // 6、显示窗口 win.setLocationRelativeTo(null); win.setSize(300,400); win.setVisible(true); btn1.addActionListener(new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { /** 这里只能传送枚举类中定义的4个对象之一,写其他会报错! 不像常量做信息标志和分类时,这里随意写,不会报错 */ move(Orientation.UP) ; // 让玛丽往上跳 } }); btn2.addActionListener(new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { move(Orientation.DOWN) ; // 让玛丽往下跳 } }); btn3.addActionListener(new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { move(Orientation.LEFT) ; // 让玛丽往左跑 } }); btn4.addActionListener(new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { move(Orientation.RIGHT) ; // 让玛丽往右跑 } }); } public static void move(Orientation o){ // 控制玛丽移动 switch (o) { case UP: System.out.println("玛丽往上飞了一下~~"); break; case DOWN: System.out.println("玛丽往下蹲一下~~"); break; case LEFT: System.out.println("玛丽往左跑~~"); break; case RIGHT: System.out.println("玛丽往→跑~~"); break; } } }
6.抽象类
6.1 抽象类概述
-
抽象类
- 某个父类知道其所有子类要完成某功能,但是每个子类完成情况都不一样,父类就只定义该功能的基本要求,具体实现由子类完成,这个类就可以是一个抽象类,抽象类其实就是一种不完全的设计图。
- 抽象类必须使用 abstract 修饰。
修饰符 abstract class 类名{ }
-
抽象方法:
- 就是抽象类中定义的子类必须完成的功能的基本要求。
- 没有方法体,只有方法签名,必须 abstract 修饰。
修饰符 abstract 返回值类型 方法名称(形参列表);
public abstract class Animal{ public abstract void run(); // 动物都要跑,具体跑的怎么样就看具体的了。 }
-
那么定义抽象方法有什么用?
- 以后再去定义子类,就一定要完成这个 run 功能。
public class Tiger extends Animal{ //这个类继承了Animal类,必须要完成Animal类中定义的 abstract方法功能。 @Override public void run(){ System.out.println("老虎跑的贼6~~"); } }
-
抽象的使用总结与注意事项
- 抽象类可以理解成类的不完整设计图,是用来被子类继承的。
- 一个类如果继承了抽象类,那么这个类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
-
总结:
- 抽象类的作用是什么样的?
- 可以被子类继承、充当模板的、同时也可以提高代码复用。
- 抽象方法是什么样的?
- 只有方法签名,没有方法体,使用了abstract修饰。
- 继承抽象类有哪些要注意?
- 一个类如果继承了抽象类,那么这个类必须重写完抽象类的全部抽象方法。
- 否则这个类也必须定义成抽象类。
- 抽象类的作用是什么样的?
6.2 抽象类的案例
-
系统需求
- 某加油站推出了2种支付卡,一种是预存10000的金卡,后续加油享受8折优惠,另一种是预存5000的银卡 ,后续加油享受8.5折优惠。
- 请分别实现2种卡片进入收银系统后的逻辑,卡片需要包含主人名称,余额,支付功能。
-
分析实现
- 创建一张卡片父类:定义属性包括主人名称、余额、支付功能(具体实现交给子类)
- 创建一张白金卡类:重写支付功能,按照原价的8折计算输出。
- 创建一张银卡类:重写支付功能,按照原价的8.5折计算输出。
/**
抽象父类
*/
public abstract class Card {
private String name; // 主人名称
private double money; // 卡牌余额
/**
子类一定要支付的,但是每个子类支付的情况不一样,所以父类把支付定义成抽象方法,交给具体子类实现
*/
public abstract void pay(double money); // 卡牌要支付的钱
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public double getMoney() {return money;}
public void setMoney(double money) {this.money = money;}
}
/**
金卡
*/
public class GoldCard extends Card{
@Override
public void pay(double money) {
// 优惠后的金额算出来:
double rs = money * 0.8; // 银卡就是这里" *0.8"换成"* 0.85"就不单独列代码了
double lastMoney = getMoney() - rs;
System.out.println(getName() + "当前账户总金额:"
+ getMoney() +",当前消费了:" + rs +",消费后余额剩余:" +
lastMoney);
setMoney(lastMoney); // 更新账户对象余额
}
}
/**
测试
*/
public class Test {
public static void main(String[] args) {
GoldCard c = new GoldCard();
c.setMoney(10000); // 父类的
c.setName("三石");
c.pay(300);
// 上述结果输出是:三石当前账户总金额:10000.0,当前消费了:240.0,消费后余额剩余:9760.0
System.out.println("余额:" + c.getMoney()); //输出结果为: 余额:9760.0
}
}
6.3 抽象类的特征、注意事项
-
特征和注意事项
-
有得有失: 得到了抽象方法,失去了创建对象的能力。
-
抽象类为什么不能创建对象?【重点】
- 因为它没有构造器?——不是的,抽象类是有构造器的,且一定要有构造器!因为抽象类是被人继承的,子类是一定要调用父类的构造器的!
- 抽象方法本身是不可以创建对象的,它本身是一个抽象模板,一般是子类继承,然后让子类负责做事情。它本身是不需要创建对象!抽象本身意味着不可以实例化。
-
类有的成员(成员变量、方法、构造器)抽象类都具备。
-
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
-
一个类继承了抽象类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
-
不能用 abstract 修饰变量、代码块、构造器。
-
-
final 和 abstract 是什么关系?[面试]
-
互斥关系
-
abstract定义的抽象类作为模板让子类继承,final定义的类不能被继承。
-
抽象方法定义通用功能让子类重写,final定义的方法子类不能重写。
6.4 抽象类的应用知识:模板方法模式
-
接触到的第二种设计模式,接触到的第一种设计模式是单例设计模式。
-
什么时候使用模板方法模式
- 使用场景说明:当系统中出现同一个功能多处在开发,而该功能中大部分代码是一样的,只有其中部分可能不同的时候。
-
模板方法模式实现步骤
- 把功能定义成一个所谓的模板方法,放在抽象类中,模板方法中只定义通用且能确定的代码。
- 模板方法中不能决定的功能定义成抽象方法让具体子类去实现。
-
案例:银行利息结算系统
-
需求:
- 某软件公司要为某银行的业务支撑系统开发一个利息结算系统,账户有活期和定期账户两种,
- 活期是0.35%,定期是 1.75%,定期如果满10万额外给予3%的收益。
- 结算利息要先进行用户名、密码验证,验证失败直接提示,登录成功进行结算。
-
分析:
- 创建一个抽象的账户类Account作为父类模板,提供属性(卡号,余额)
- 在父类Account中提供一个模板方法实现登录验证,利息结算、利息输出。
- 具体的利息结算定义成抽象方法,交给子类实现。
- 定义活期账户类,让子类重写实现具体的结算方法
- 定义定期账户类,让子类重写实现具体的结算方法
- 创建账户对象,完成相关功能。
// Account.java public abstract class Account { private String cardId; private double money; public Account() { } public Account(String cardId, double money) { this.cardId = cardId; this.money = money; } /** 模板方法 */ public final void handle(String loginName , String passWord ){ // a.判断登录是否成功 if("itheima".equals(loginName) && "123456".equals(passWord)){ System.out.println("登录成功。。"); // b.正式结算利息 // 当前模板方法知道所有子类账户都要结算利息,但是具体怎么结算,模板不清楚,交给具体的子类来计算 double result = calc(); // c.输出利息详情 System.out.println("本账户利息是:"+ result); }else{ System.out.println("用户名或者密码错误了"); } } public abstract double calc(); public String getCardId() {return cardId;} public void setCardId(String cardId) {this.cardId = cardId;} public double getMoney() {return money;} public void setMoney(double money) {this.money = money;} }
// CurrentAccount.java /** 活期账户 */ public class CurrentAccount extends Account { public CurrentAccount(String cardId, double money) { super(cardId, money); } @Override public double calc() { // b.正式结算利息 double result = getMoney() * 0.0175; // 结算利息了 return result; } }
// Test.java public class Test { public static void main(String[] args) { CurrentAccount acc = new CurrentAccount("ICBC-111", 100000); acc.handle("itheima", "123456"); } }
-
- 模板方法我们是建议使用final修饰的,这样会更专业,那么为什么呢?
- 答:模板方法是给子类直接使用的,不是让子类重写的,一旦子类重写了模板方法就失效了。
- 模板方法模式解决了什么问题?
- 极大的提高了代码的复用性
- 模板方法已经定义了通用结构,模板不能确定的定义成抽象方法。
- 使用者只需要关心自己需要实现的功能即可。
7.接口
7.1 接口概述、特点
-
什么是接口?(重点关注接口的语法)
- 接口就是体现规范的,其中用抽象方法定义的一组行为规范,接口是更加彻底的抽象。
-
体现了现实世界中"如果你是这类事物…则必须完成某些行为…"的思想。
-
接口的格式如下:
接口用关键字interface来定义 public interface 接口名 { // 常量 // 抽象方法 }
-
JDK8之前接口中只能是抽象方法和常量,没有其他成分了。
-
接口不能实例化【即 创建对象】。
-
接口中的成员都是 public 修饰的,写不写都是,因为规范的目的是为了公开化。
/**
接口
*/
public interface SportManInterface {
// 接口中的成员:JDK 1.8之前只有常量 和 抽象方法
// 1、常量
// public static final 可以省略不写,接口默认会为你加上!
// public static final String SCHOOL_NAME = "黑马";
String SCHOOL_NAME = "黑马"; // 不是成员变量,是常量,必须初始化值!
// 2、抽象方法
// public abstract 可以省略不写,接口默认会为你加上!
// public abstract void run();
void run();
// public abstract void eat();
void eat();
}
7.2 接口的基本使用:被实现
-
接口的用法:
-
接口是用来被类实现(implements)的,实现接口的类称为实现类。实现类可以理解成所谓的子类,接口理解成所谓的特殊父类。
修饰符 class 实现类 implements 接口1, 接口2, 接口3 , ... { } 实现的关键字:implements
-
从上面可以看出,接口可以被类单实现,也可以被类多实现。
-
实现相当于特殊的继承。
public interface Law { void rule(); // 遵章守法 }
public interface SportMan { void run(); void competition(); }
/** 实现类(子类) */ public class PingPongMan implements SportMan , Law{ private String name; public PingPongMan(String name) { this.name = name; } @Override public void rule() { System.out.println(name + "要遵章守法,不能随意外出,酗酒,约会~~~"); } @Override public void run() { System.out.println(name + "必须要跑步训练~~"); } @Override public void competition() { System.out.println(name + "需要参加国际比赛~~"); } }
-
-
接口实现的注意事项:
- 一个类实现接口,必须重写完全部接口的全部抽象方法,否则这个类需要定义成抽象类。
7.3 接口与接口的关系:多继承
-
基本小结
- 类和类的关系:单继承。
- 类和接口的关系:多实现。
- 接口和接口的关系:多继承,一个接口可以同时继承多个接口。
-
接口多继承的作用
- 规范合并,整合多个接口为同一个接口,便于子类实现。
/** 实现类 */ // public class BasketballMan implements Law, SportMan, People { // 让SportMan接口继承Law和People接口 // 继承的好处就是可以让这里 直接实现SportMan接口即可一样实现4个方法,不用像上面那样实现三个接口 public class BasketballMan implements SportMan{ @Override public void rule() { } @Override public void eat() { } @Override public void run() { } @Override public void competition() { } }
public interface SportMan extends Law, People { void run(); void competition(); }
public interface Law { void rule(); // 遵章守法 void eat(); }
public interface People { void eat(); }
-
接口支持多继承,类不支持多继承,为什么?
- 类不支持多继承,是因为一类继承两个父亲,两个父亲中可能会出现相同的方法,子类就不知道调用A父亲还是B父亲了。
- A接口和B接口有相同的方法,不妨碍在子类的时候只用重写一个该方法【规范合并】。
-
规范冲突的时候,就不能实现继承!
7.4 JDK8开始接口新增方法(了解)
-
JDK8版本开始后,Java只对接口的成员方法进行了新增。
-
原因如下:
-
方法体:
-
第一种:默认方法
- 类似之前写的普通实例方法:必须用 default修饰。
- 默认会public修饰。需要用接口的实现类的对象来调用。
default void run(){ System.out.println("--开始跑--"); }
-
第二种:静态方法
- 默认会 public修饰,必须static修饰。
- 注意:接口的静态方法必须用本身的接口名来调用。
static void inAdrr(){ System.out.println("--学Java--"); }
-
第三种:私有方法
- 就是私有的实例方法,必须使用 private修饰,从JDK 1.9才开始有的。
- 只能在本类中被其他的默认方法或者私有方法访问。
private void go(){ System.out.println("--准备--"); }
public interface SportManInter { /** 1、JDK 8开始 :默认方法(实例方法) -- 必须default修饰,默认用public修饰 -- 默认方法,接口不能创建对象,这个方法只能过继给了实现类,由实现类的对象调用。 */ default void run(){ go(); //内部被其他默认方法调用 System.out.println("跑的很快~~~"); } /** 2、静态方法 必须使用static修饰,默认用public修饰 -- 接口的静态方法,必须接口名自己调用。 */ static void inAddr(){ System.out.println("我们都在学习Java新增方法的语法,它是Java源码自己会用到的~~~"); } /** 3、私有方法(实例方法) -- JDK 1.9开始才支持的。 -- 必须在接口内部才能被访问 */ private void go(){ System.out.println("开始跑~~~"); } } class PingPongMan implements SportManInter{ } class Test{ public static void main(String[] args) { // 默认方法的调用——类实例化 PingPongMan p = new PingPongMan(); p.run(); // 静态方法的调用——接口名调用 SportManInter.inAdrr(); // 私有方法必须在接口内部才能被访问 这里是在默认方法接口中调用私有方法 } }
- JDK 8新增的3种方法我们自己在开发中很少使用,通常是Java源码涉及到。现阶段需要理解、识别语法,明白调用关系即可。
-
7.5 使用接口的注意事项(了解)
-
接口不能创建对象
- 接口更加彻底的抽象。无抽象方法,无构造器。
-
一个类实现多个接口,多个接口中有同样的静态方法不冲突。
- 接口不允许子类去调用方法的,因此不会冲突。
- 接口的静态方法,只能接口自己调用。A调用A的,B调用B的。
-
一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的。
-
一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可。
-
一个接口继承多个接口,是没有问题的,如果多个接口中存在规范冲突则不能多继承。