本周学习内容目录
一.ATM系统项目的实现
1.启动ATM系统,展示欢迎页面
public void start() {
while (true) {
System.out.println("===欢迎您进入ATM银行系统===");
System.out.println("1.用户登录");
System.out.println("2.用户开户");
System.out.println("请选择你的操作 :");
int command = scanner.nextInt();
switch (command) {
case 1:
// 用户登录
login();
break;
case 2:
// 用户开户
createAccount();
break;
default:
System.out.println("没有此命令,请重试");
}
}
}
2. 完成用户开户操作
1.创建一个账户对象,用于封装对象的开户信息
2.需要用户输入自己的开户信息,赋值给账户对象
3.把这个用户对象,存入到对象集合中去
/** 完成用户开户操作 */
private void createAccount(){
System.out.println("==系统开户操作==");
// 1.创建一个账户对象,用于封装用户的开户信息
Account account = new Account();
// 2.需要用户输入自己的开户信息,赋值给账户对象
System.out.println("请您输入你的账户名称 ");
String name = scanner.next();
account.setUserName(name);
while (true) {
System.out.println("请您输入您的性别 :");
char sex = scanner.next().charAt(0);
if (sex == '男' || sex == '女'){
account.setSex(sex);
break;
}else {
System.out.println("你输入的性别有误 !");
}
}
while (true) {
System.out.println("请你输入你的账户密码 ");
String passWord = scanner.next();
System.out.println("请您确认你的密码 :");
String passWord2 = scanner.next();
// 判断两次密码是否一样
if (passWord2.equals(passWord)){
account.setPassWord(passWord2);
break;
}else {
System.out.println("两次密码不一致,请重试");
}
}
System.out.println("输入每次的取现额度 :");
double limit = scanner.nextDouble();
account.setLimit(limit);
// 重点:我们需要为这个账户生成一个卡号(有系统自动生成,8位数字表示:不能与其他账户的卡号重复)
String newCardId = createCardId();
account.setCardId(newCardId);
// 3.把这个用户对象,存入到账户集合中去
accounts.add(account);
System.out.println("恭喜你" + account.getUserName() + "开户成功。你的卡号是:" + account.getCardId());
}
4.需要为这个账号生成一个卡号(由系统自动生成,8位数字表示,且不能与其他卡号重复)(先随机一个8位数的卡号,在根据卡号查询对象返回进行判断此卡号是否重复)
// 返回一个随机8位数的卡号,且不能与其他卡号重复
private String createCardId(){
// 1.定义一个String类型的变量记住一个8位数的卡号
while (true) {
String cardId = "";
// 2.定义循环,循环8次,每次产生的随机数都给cardId连起来
Random r = new Random();
for (int i = 0; i < 8; i++) {
int data = r.nextInt(10);
cardId += data;
}
// 3.判断卡号是否重复
Account account = getAccountByCardId(cardId);
if (account == null){
// 说明cardId没有找到账号对象,因此没有与其他账号的卡号重复,可以返回它成为一个新卡号
return cardId;
}
}
}
// 根据卡号查询账户对象返回
private Account getAccountByCardId(String cardId){
// 遍历全部的账户对象
for (int i = 0; i < accounts.size(); i++) {
Account account = accounts.get(i);
// 判断这个账户对象acc中的卡号是否是我们要找的卡号
if (account.getCardId().equals(cardId)){
return account;
}
}
return null;
}
3.完成用户的登录操作
1.先判断系统中是否存在账户对象,如果存在,才能登录,如果不存在,我们直接结束登录操作
2.进行登录操作,输入账户的卡号
3.判断账户卡号是否存在
4.卡号存在,则输入登录密码
5.判断密码是否正确,正确的话,则登录成功
/** 完成用户的登录操作 */
private void login() {
System.out.println("==系统登录==");
// 1.判断系统中是否存在账户对象,存在才能登录,如果不存在,我们直接结束登录操作
if (accounts.size() == 0) {
System.out.println("当前系统中无任何账户。请先开户");
return;
}
// 2.系统中存在用户对象,可以开始进行登录操作
System.out.println("请您输入您的卡号 :");
String cardId = scanner.next();
// 3.判断卡号是否存在
Account account = getAccountByCardId(cardId);
if (account == null) {
// 卡号不存在
System.out.println("您输入的卡号不存在,请重试");
} else {
// 卡号存在
while (true) {
System.out.println("请您输入你的登录密码;");
String passWord = scanner.next();
// 判断密码是否正确
if (account.getPassWord().equals(passWord)) {
loginAccount = account;
// 密码正确,登录成功
System.out.println("恭喜您" + account.getUserName() + "登录成功,您的卡号是:" + account.getCardId());
// 展示登录后的操作界面了
showUserCommand();
return;// 跳出并结束登录后的操作页面
} else {
System.out.println("你输入的密码有误-");
}
}
}
}
6展示当前登录的账户信息
/** 展示当前登录的账户信息 */
private void showLoginAccount(){
System.out.println("==当前您的账户信息如下==");
System.out.println("卡号:" + loginAccount.getCardId());
System.out.println("户主:" + loginAccount.getUserName());
System.out.println("户主性别:" + loginAccount.getSex());
System.out.println("余额:" + loginAccount.getMoney());
System.out.println("每次取现额度:" + loginAccount.getLimit());
}
7.展示登录后的操作页面
/** 展示登录后的操作界面 */
private void showUserCommand(){
while (true) {
System.out.println(loginAccount.getUserName() + "您可以选择以下操作 ");
System.out.println("1.查询账户");
System.out.println("2.存款");
System.out.println("3.取款");
System.out.println("4.转账");
System.out.println("5.密码修改");
System.out.println("6. 退出");
System.out.println("7.注销当前账户");
System.out.println("请选择:");
int command = scanner.nextInt();
switch (command){
case 1:
// 查询账户
showLoginAccount();
break;
case 2:
// 存款
depositMoney();
break;
case 3:
// 取款
drawMoney();
break;
case 4:
// 转账
transfarMoney();
break;
case 5:
// 密码修改
updatePassWord();
return;// 跳出并结束当前方法
case 6:
// 退出
System.out.println(loginAccount.getUserName() + "退出系统成功");
return;// 跳出并结束当前方法
case 7:
// 注销当前账户
if(deleteAccount()){
// 销户成功,回到欢迎页面
return;
}
break;
default:
System.out.println("您输入的命令有误,请重试");
}
}
}
4.存钱
1.输入存款金额
2.更新当前账户的余额
/** 存钱 */
private void depositMoney() {
System.out.println("==存款操作==");
System.out.println("请您输入存款金额");
double money = scanner.nextDouble();
// 更新当前账户的余额
loginAccount.setMoney(loginAccount.getMoney() + money);
System.out.println("恭喜您,存钱" + money + "成功,先余额为" + loginAccount.getMoney());
}
5.取款
1.先判断余额是否大于等于100元,如果不足,则不允许取钱
2.判断余额是否足够
3.判断取款金额是否小于等于取款限额
4.更新账户余额
/** 取款 */
private void drawMoney(){
System.out.println("==取款操作==");
// 1.判断账户余额是否达到100元,如果不足100元,则不允许账户取钱
if (loginAccount.getMoney() < 100){
System.out.println("您的余额不足100元,不允许取钱");
return;
}
// 2.判断余额是否足够
while (true) {
System.out.println("请输入取款金额");
double getMoney = scanner.nextDouble();
// 3.判断余额是否足够
if (loginAccount.getMoney() >= getMoney){
// 余额足够
// 4.判断取款金额是否超过单次取款限度
if (getMoney > loginAccount.getLimit()){
System.out.println("此次取款超过单次取款额度,你每次最多取款" + loginAccount.getLimit());
}else {
loginAccount.setMoney(loginAccount.getMoney() - getMoney);
System.out.println("恭喜你,取款" + getMoney + "余额还有" +loginAccount.getMoney());
// System.out.println("恭喜您,取款成功,您的剩余余额是" + (loginAccount.getMoney() - getMoney) );
break;
}
} else {
System.out.println("你的余额不足,余额为" + loginAccount.getMoney());
}
}
}
6.转账
1.要先判断ATM系统中是否存在两个账户
2.然后判断自身账户中是否有钱
3.可以开始转账了
4.输入对方的卡号
5.验证对方的账号是否存在
6.若对方账号存在,则继续验证对方的姓氏
7.然后判断转账金额是否大于自身账户金额,若大于,则更新自身和对方账户的余额
/** 转账 */
private void transfarMoney() {
System.out.println("==用户转账==");
// 1.判断系统中是否有两个账户
if (accounts.size() < 2){
System.out.println("当前系统中只要你一个账户,无法进行转账操作");
}
// 2.判断自身账户中是否有钱
if (loginAccount.getMoney() == 0){
System.out.println("自身账户没钱,不能转账");
return;
}
// 3.真正可以开始转账了
while (true) {
System.out.println("请输入要转账用户的卡号");
String cardId = scanner.next();
// 4.判断账户卡号是否存在
Account account = getAccountByCardId(cardId);
if (account == null){
System.out.println("您输入的卡号不存在,请重试");
}else{
// 对方的账户存在,继续让用户验证姓氏
String name = '*' + account.getUserName().substring(1);
System.out.println("请您输入【" + name + "】的姓氏 ");
String preName = scanner.next();
// 5.判断认证的姓氏是否正确
if (account.getUserName().startsWith(preName)){
// 姓氏认证通过了,可以开始转账了
while (true) {
System.out.println("请输入转款金额 :");
double money = scanner.nextDouble();
// 6.判断转账金额是否超出自身余额
if (loginAccount.getMoney() >= money){
// 可以转账
// 更新自身的账户余额
loginAccount.setMoney(loginAccount.getMoney() - money);
// 更新对方的账户余额
account.setMoney(account.getMoney() + money);
System.out.println("恭喜你,转账成功!");
return;
}else {
System.out.println("余额不足,不能转账那么多,最多可转" + loginAccount.getMoney());
}
}
}else{
System.out.println("您认证的姓氏有误,请重试");
}
}
}
}
7.销户
1.确认是否进行销户操作
2.查看账户中是否有钱,有的话,不允许销户
/** 消除账户 */
private boolean deleteAccount() {
System.out.println("==销户操作==");
// 1.问问用户是否真的要进行销户操作
System.out.println("您真的要进行销户吗 y / n");
String command = scanner.next();
switch (command){
case "y":
// 进行销户
// 2.判断用户卡中是否还有余额,如果有,则不允许销户
if (loginAccount.getMoney() == 0){
// 销户成功
accounts.remove(loginAccount);
System.out.println("您的账户已经销户");
return true;
}else {
// 卡中有余额,不允许销户,返回操作页面
System.out.println("对不起,你的账户中存在余额,无法销户");
return false;
}
default:
System.out.println("好的,您的账户保留 ");
return false;
}
}
8.密码修改
1.输入当前账户的密码
2.输入账号的新密码
3.再次输入账号的新密码
4. 比较两次新密码是否一致,一致则密码修改成功
/** 账户密码修改 */
private void updatePassWord() {
System.out.println("==账户密码修改操作==");
//
while (true) {
System.out.println("请输入当前账户的密码 :");
String passWord = scanner.next();
if (loginAccount.getPassWord().equals(passWord)){
// 认证通过,
while (true) {
System.out.println("请输入您账户的新密码");
String newPassWord = scanner.next();
System.out.println("请再一次输入您账户的新密码");
String newPassWord2 = scanner.next();
if (newPassWord2.equals(newPassWord)){
loginAccount.setPassWord(newPassWord);
System.out.println("您账户的密码已经修改成功");
return;
}else {
System.out.println("您输入的两次密码不一致,请重试");
}
}
}else {
System.out.println("你输入的此账号当前的密码有误");
}
}
}
二.面对对象高级一
1.static修饰符
1.类变量
有static修饰,属于类,在计算机中只有一份,被类的全部对象共享
// 1.类变量的用法
// 类名.类变量(推荐)
Student.name = "袁华";
// 对象.类变量(不推荐)
Student s1 = new Student();
s1.name = "马冬梅";
Student s2 = new Student();
s2.name = "秋雅";
System.out.println(s1.name); // 秋雅
System.out.println(Student.name); // 秋雅
2.实例变量(对象的变量)
无static修饰,属于每个对象的
// 2.实例变量的用法:属于每个对象的变量
// 对象.实例变量
s1.age = 23;
s2.age = 18;
System.out.println(s1.age); // 23
// System.out.println(Student.age); // 报错
3.类方法
// 类方法
public static void printHelloWorld(){
System.out.println("Hello World");
System.out.println("Hello World");
}
// 1.类方法的用法
// 类名.类方法(推荐)
Student.printHelloWorld();
// 对象.类方法(不推荐)
Student s = new Student();
s.printHelloWorld();
4.实例方法
// 实例方法(对象的方法)
public void printPass(){
System.out.println("成绩" +
(score >= 60 ? "及格" : "不及格"));
// 2.实例方法的用法
// 对象.实例方法
s.printPass();
// Student.printPass(); // 报错
5.使用类方法和实例方法的注意事项
public class Student {
static String schoolName;// 类变量
double score;
// 1.类方法中可以直接访问类的成员,不可以直接访问实例成员
public static void printHelloWorld(){
// 注意:同一个类中,访问类成员,可以省略类名不写
schoolName = "黑马";
printHelloWorld2();
// System.out.println(score); // 会报错的
// printPass(); // 报错的
// System.out.println(this); // 报错
}
// 类方法
public static void printHelloWorld2(){
}
// 2.实例方法中既可以直接访问类成员,也可以直接访问实例成员
// 实例方法
// 3.实例方法中可以出现this关键字,类方法中不可以出现this关键字的
public void printPass(){
schoolName = "黑马2";
printHelloWorld2();
System.out.println(score);
printPass2();
System.out.println(this);
}
// 实例方法
public void printPass2(){
}
}
6.代码块
1.静态代码块
// 格式:static{}
// 特点:类加载时自动执行,由于类只会加载一次,所有静态代码块也只会执行一次
// 作用:完成类的初始化,例如:对类变量的初始化赋值
static {
System.out.println("静态代码块执行了");
schoolName = "黑马";
}
2.实例代码块
// 格式{}
// 特点:每次创建对象时,执行实例代码块,并在构造器前执行
// 作用:和构造器一样,都是用来完成对象的初始化的,例如:对实例变量进行初始化赋值
{
System.out.println("实例代码块执行了");
}
public Student() {
System.out.println("无参数构造器执行了");
}
public Student(String name) {
System.out.println("有参数构造器执行了");
}
}
下面这是随机生成验证码的一段代码块,可以直接调用,减少了重复代码的编写
public class MyUtil {
// 工具类不需要创建对象,建议将工具类的构造器私有化
private MyUtil(){
}
public static String createCode(int n) {
String code = "";
String data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random r = new Random();
// 2.开始定义一个循环产生每位随机字符
for (int i = 0; i < n; i++) {
// 3.随机一个字符范围内的索引
int index = r.nextInt(data.length());
// 4.根据索引在全部字符中提取该字符
code += data.charAt(index); // code = code + 字符
}
return code;
}
}
- 上述代码可以直接调用,如:
public class LoginDemo {
public static void main(String[] args) {
System.out.println(MyUtil.createCode(4));
}
}
public class RegisterDemo {
public static void main(String[] args) {
System.out.println(MyUtil.createCode(6));
}
}
2.继承
认识继承
(B类可以通过extends继承A类,则B类为子类,A类为父类)
public class B extends A{
}
继承的特点
父类用private私有化的不能被继承
继承的好处
继承的两个注意事项
- Java是单继承的,一个类只能继承一个直接父类,Java中的类不支持多继承,但是支持多层继承
- Object是Java中所有祖宗的类
3.不同权限修饰符的使用(就是在哪能访问,在哪不能方法的问题)
public class Fu {
// 1.私有:只能在本类中访问
private void privateMethod(){
System.out.println("==private==");
}
// 2.缺省:本类,同一个包下的类
void method(){
System.out.println("==缺省==");
}
// 3.protected:本类,同一个包下的类,任意包下的子类
protected void protectedMethod(){
System.out.println("==protected==");
}
// 4.public: 本类,同一个包下的类,任意包下的子类,任意包下的任意类
public void publicMethod(){
System.out.println("==public==");
}
public void test(){
privateMethod();
method();
protectedMethod();
publicMethod();
}
}
4.方法重写
什么是方法重写
- 当子类决定父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称,参数列表一样的方法,去覆盖父类的这个方法
- 注意;重写后,方法的访问,Java会遵循就近原则
重写前的方法
public class A {
public void print1(){
System.out.println("111");
}
public void print2(){
System.out.println("111111");
}
}
重写后的方法
public class B extends A {
// 方法重写
@Override
public void print1(){
System.out.println("666");
}
@Override
public void print2(){
// 方法重写
System.out.println("666666");
}
}
方法重写的其他注意事项
1.要使用Override注释,它可以指定Java编辑器,检查我们方法重写的格式是否正确,代码可读性也会更好
2。子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限(public > protected > 缺省)
3.重写的方法返回值类型,必须与别重写方法的返回值类型一样,或者范围更小
4.私有方法,静态方法不能被重写,如果重写会报错的
子类中访问其他成员变量的特点:就近原则
启动项
public class Test {
public static void main(String[] args) {
// 目标:掌握子类中访问其他成员的特点:就近原则
Z z = new Z();
z.showName();
}
}
public class Z extends F {
String name = "子类名称";
public void showName(){
String name = "局部名称";
System.out.println(name); // 局部名称
System.out.println(this.name); // 子类成员变量,访问子类的成员变量时要这样写
System.out.println(super.name);// 父类的成员变量
}
访问子类成员变量时
System.out.println(this.name);
访问父类成员变量时
System.out.println(super.name);
如果直接访问,则是局部的
public void showName(){
String name = "局部名称";
System.out.println(name);
}
在子类方法中访问其他成员(成员变量,成员方法),时按照就近原则的
先子类局部范围找
然后子类成员范围找
然后父类成员范围找,如果父类范围还没有找到则报错
如果子父类中,出现了重名的成员,会优先使用子类的,如果一定要在子类中使用父类的怎么办?
可以通过super关键字,指定访问父类的成员:super。父类成员变量/父类成员方法
5.构造器
调用子类的构造器时会先调用父类的无参数构造器
class F{
public F() {
System.out.println("==父类F的无参数构造器执行了==");
}
}
class Z extends F{
public Z(){
// super();// 默认存在的
System.out.println("==子类Z的无参数构造器执行了==");
}
public Z(String name){
// super();// 默认存在的
System.out.println("==子类Z的有参数构造器执行了==");
}
}
public class Test {
public static void main(String[] args) {
// 目标:先认识子类构造器的特点,再掌握这个特点的常见应用场景
Z z = new Z();
Z z2 = new Z("波妞");
}
}
执行结果
子类调用父类构造器的应用场景
public class Test2 {
public static void main(String[] args) {
// 目标:搞清楚子类构造器为什么要调用父类构造器,有啥应用场景
Teacher t = new Teacher("播妞",19 ,"java");
System.out.println(t.getName());
System.out.println(t.getAge());
System.out.println(t.getSkill());
}
}
class Teacher extends People{
private String skill;
public Teacher(String name,int age,String skill){
// 调用父类的构造器,(应用场景)
super("波妞",17);
this.skill = skill;
}
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
}
class People{
private String name;
private int age;
public People() {
}
public People(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
通过this(…)调用兄弟构造器
public class Test3 {
public static void main(String[] args) {
// 目标:掌握在类中的构造器,通过this(。。。)调用兄弟构造器的作用
Student s1 = new Student("波仔",19,"淮中");
// 需求:如果学生没有填写学校,那么学校默认就是黑马程序员
Student s2 = new Student("里斯",23);
System.out.println(s1.getName());
System.out.println(s1.getAge());
System.out.println(s1.getSchoolName());
System.out.println(s2.getName());
System.out.println(s2.getAge());
System.out.println(s2.getSchoolName());
}
}
class Student{
private String name;
private int age;
private String schoolName;
public Student() {
}
public Student(String name,int age){
// this.name = name;
// this.age = age;
// this.schoolName = "黑马程序员";
this(name,age,"黑马程序员");
}
public Student(String name, int age,String schoolName) {
this.name = name;
this.age = age;
this.schoolName = schoolName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSchoolName() {
return schoolName;
}
public void setSchoolName(String schoolName) {
this.schoolName = schoolName;
}
}
三.面向对象高级二
1. 多态
(1)前提
有继承或实现关系,存在父类引用子类对象,存在方法重写
对象多态
public class Test {
public static void main(String[] args) {
People p1 = new Teacher();
p1.run();// 识别技巧:编译看左边,运行看右边
System.out.println(p1.name);// 注意:对于变量,编译看左边,运行也看左边
People p2 = new Student();
p2.run();// 识别技巧:编译看左边,运行看右边
System.out.println(p2.name);// 注意:对于变量,编译看左边,运行也看左边
}
}
多态的好处
public class Test {
public static void main(String[] args) {
// 目标:理解多态的好处
// 好处1:可以实现解耦合,右边对象可以随时切换,后续业务随机应变
People p1 = new Student();
p1.run();
// p1.test();// 多态下存在的问题:无法直接调用子类的独有功能
// 强制类型转换
Student s1 = (Student)p1;
s1.test();
// 强制类型转换可能存在的问题:编译阶段有继承或者实现关系可以强制类型转换,但是运行时可能出现强制类型转换异常
// Teacher t1 = (Teacher)p1; // 运行时出现了:ClassCastException
if (p1 instanceof Student){
Student s2 = (Student)p1;
s2.test();
} else if (p1 instanceof Teacher) {
Teacher t2 = (Teacher)p1;
t2.teach();
}
System.out.println("-------------------");
// 好处2:可以使用父类类型的变量作为形参,可以接受一切子类对象
Student s = new Student();
go(s);
Teacher t = new Teacher();
go(t);
}
public static void go(People p){
p.run();
if (p instanceof Student){
Student s = (Student) p;
s.test();
} else if (p instanceof Teacher) {
Teacher t = (Teacher) p;
t.teach();
}
}
}
-上面有个强制类型转换
// 强制类型转换
Student s1 = (Student)p1;
s1.test();
// 强制类型转换可能存在的问题:编译阶段有继承或者实现关系可以强制类型转换,但是运行时可能出现强制类型转换异常
// Teacher t1 = (Teacher)p1; // 运行时出现了:ClassCastException
if (p1 instanceof Student){
Student s2 = (Student)p1;
s2.test();
} else if (p1 instanceof Teacher) {
Teacher t2 = (Teacher)p1;
t2.teach();
}
2.final的使用
注意:
final修饰基本类型的变量,变量存储的数据不能被改变
final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向的内容
常量:通过static final修饰的成员变量被称为常量
常量的命名规范: 建议使用大写英文字母,多个单词使用下划线连接起来
3. 抽象类
认识抽象类和其特点
抽象类中可以不写抽象方法,但有抽象方法的类一定是抽象类
类有的成员(成员变量,方法,构造器)抽象类都具备
抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现
一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义为抽象类
抽象方法
public abstract class A {
// 抽象方法:必须用abstract修饰,只要方法签名,一定不能有方法体
public abstract void run();
}
抽象类的好处
更好的支持多态
abstract class Shape {
// 抽象类中的方法一般要求都是抽象方法,抽象方法没有方法体
abstract void draw();
}
// 当一个普通类继承一个抽象类后,这个普通类必须重写抽象类中的方法
class Cycle extends Shape {
@Override
void draw() { // 重写抽象类中的draw方法
System.out.println("画一个圆圈");
}
}
public class Test4 {
public static void main(String[] args) {
//Shape shape = new Shape(); 抽象类虽然不能直接实例化
// 但可以把一个普通类对象传给一个抽象类的引用呀,即父类引用指向子类对象
Shape shape = new Cycle(); // 这称作:向上转型
/*Cycle cycle = new Cycle();
Shape shape = cycle // 这是向上转型的另一种写法
*/
shape.draw(); // 通过父类引用调用被子类重写的方法
}
}
抽象类的应用场景之一
经常用来设计模板方法设计模式
public class Test {
public static void main(String[] args) {
// 目标:搞清楚抽象类的应用场景之一:经常用来设计模板方法设计模式
// 场景:学生,老师都要写一篇作文:我的爸爸
// 第一段是一样的
// 正文部分自由发挥
// 最后一段也是一样的
Teacher t = new Teacher();
t.write();
Student s = new Student();
s.write();
}
}
abstract class People {
/*
设计模板方法设计模式
1.定义一个模板方法出来
*/
public final void write(){
System.out.println("\t\t\t\t\t《我的爸爸》");
System.out.println("\t\t我的爸爸好啊,牛逼啊,来看看我的爸爸有多牛");
// 2.模板方法并不清楚正文部分到底应该怎么写,但是它知道子类肯定要写
System.out.println(writeMain());
System.out.println("有这样的爸爸真好");
}
// 3.设计一个抽象方法写正文,具体的实现交给子类来完成
public abstract String writeMain();
}
class Student extends People {
@Override
public String writeMain(){
return "我的爸爸很牛";
}
}
class Teacher extends People{
@Override
public String writeMain(){
return "我的爸爸很好";
}
}
4.接口
(1)概述
抽象类是从多个类中抽象出来的模板,如果将这种抽象进行的更彻底,则可以提炼出一种更加特殊的“抽象类”——接口(Interface)
使用接口的好处
public class Test {
public static void main(String[] args) {
// 目标:搞清楚使用接口的好处
/*
弥补了类单继承的不足,一个类同时可以实现多个接口
让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现
*/
Driver d = new A();
d.drive();
Singer s = new A();
s.sing();
Student s2 = new A();
s2.student();
}
}
class A extends Student implements Driver,Singer{
@Override
public void drive() {
System.out.println("回开车");
}
@Override
public void sing() {
System.out.println("会唱歌");
}
}
class Student{
void student(){
System.out.println("我是一个学生");
}
}
interface Driver{
void drive();
}
interface Singer{
void sing();
}
接口的多继承
能够用来有更少的代码量来实现更多的功能
public class Test {
public static void main(String[] args) {
// 目标: 理解接口的多继承,作用:便于实现类去实现
}
}
interface A{
void test1();
}
interface B{
void test2();
}
interface C{}
// 接口是多继承的
interface D extends C,B,A{
}
class E implements D{
@Override
public void test1() {
}
@Override
public void test2() {
}
}
接口的几点注意事项
public class Test2 {
public static void main(String[] args) {
// 目标:理解使用接口的几点注意事项
Zi z = new Zi();
z.run();
}
}
// 1.一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承
interface I{
void test1();
}
interface J{
String test1();
}
// interface K extends I,J{}// 报错,上面方法签名冲突
// 2.一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现
// 3.一个类继承了父类,有同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的
class Fu{
public void run(){
System.out.println("===父类中的run()方法执行了===");
}
}
interface IT{
default void run(){
System.out.println("===接口中的run()方法实现了===");
}
}
class Zi extends Fu implements IT{
}
// 4.一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可
interface IT1{
}
interface IT2{
}