20145308刘昊阳 《Java程序设计》第4周学习总结
教材学习内容总结
第六章 继承与多态
6.1 何谓继承
- 继承:面向对象中,为避免多个类间重复定义共同行为使用
- 把相同代码提升为父类
- 运用
extends
关键字的子类会继承扩充父类行为
package cc.openhome;public class Game1RPG { public static void main(String[] args){ demoGame1SwordsMan(); demoGame1Magician(); } static void demoGame1SwordsMan(){ Game1SwordsMan game1swordsMan = new Game1SwordsMan(); game1swordsMan.setName("Justin"); game1swordsMan.setLevel(1); game1swordsMan.setBlood(200); System.out.printf("剑士:(%s,%d,%d)%n",game1swordsMan.getName(),game1swordsMan.getLevel(),game1swordsMan.getBlood()); } static void demoGame1Magician(){ Game1Magician game1Magician = new Game1Magician(); game1Magician.setName("Monica"); game1Magician.setLevel(1); game1Magician.setBlood(100); System.out.printf("魔法师:(%s,%d,%d)%n",game1Magician.getName(),game1Magician.getLevel(),game1Magician.getBlood()); }}
package cc.openhome;public class Game1Role { private String name; private int level; private int blood; public int getBlood(){ return blood; } public void setBlood(int blood){ this.blood = blood; } public int getLevel(){ return level; } public void setLevel(int level){ this.level = level; } public String getName(){ return name; } public void setName(String name){ this.name = name; }}
package cc.openhome;public class Game1SwordsMan extends Game1Role { public void fight(){ System.out.println("挥剑攻击"); }}
package cc.openhome;public class Game1Magician extends Game1Role{ public void fight(){ System.out.println("魔法攻击"); } public void cure(){ System.out.println("魔法治疗"); }}
运行截图
- 要修改子类中继承父类的名称,只需修改父类就好,只要是继承父类的子类都无法修改
private
成员可以继承,不过子类无法直接存取,必须通过父类提供的方法来存取- 子类只能继承一个父类
- 子类与父类间有
is-a
关系- 检查
Role role1 = new SwordsMan();
:从=
右向左读,右边类应该是左边类的子类 - 可以让子类扮演(cast)父类
SwordsMan swordsMan = (SwordsMan) role1;
- 检查
- 多态:使用单一借口操作多种类型的对象
- 在
showBlood()
方法中,既可以通过Game1Role
类型操作Game1SwordsMan
变量,也可以通过Game1Role
类型操作Game1Magician
变量,避免多次重载
- 在
package cc.openhome;public class Game2RPG { public static void main(String[] args){ Game1SwordsMan game1swordsMan = new Game1SwordsMan(); game1swordsMan.setName("Justin"); game1swordsMan.setLevel(1); game1swordsMan.setBlood(200); Game1Magician game1Magician = new Game1Magician(); game1Magician.setName("Monica"); game1Magician.setLevel(1); game1Magician.setBlood(100); showBlood(game1swordsMan);//传入game1swordsMan实例,合法,该对象是一种Game1Role showBlood(game1Magician);//传入game1Magician实例,合法,该对象是一种Game1Role } static void showBlood (Game1Role game1role){//将参数声明为Game1Role型变量 System.out.printf("%s 血量 %d%n",game1role.getName(),game1role.getBlood()); }}
运行截图
重新定义行为:将接口操作相同只是操作内容不同的两个方法提升至父类中进行定义
public class Role { private String name; private int level; private int blood; public int getBlood() { return blood; } public void setBlood(int blood) { this.blood = blood; } public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void fight() { //实际上角色如何进行攻击,只有子类知道,所以子类要重新定义fight()的实际行为 }}
- 父类中定义了方法,实际上如何执行只有子类知道,父类里的方法内容是空的,子类继承父类,再对方法进行定义
public class Magician extends Role{ public void fight(){ System.out.println("魔法攻击");}}
- 重新定义:继承父类后,定义与父类中相同的方法部署,但执行内容不同,因为对父类中已定义的方法不满意,所以进行重新定义
public class SwordsMan extends Role{ public void fight(){ System.out.println("挥剑攻击");}}
运行截图
- 重新定义子类中某个方法时,子类必须撰写与父类方法中相同的签署
@Override
在某个子类前标注,表示要求编译程序检查,该方法是不是重新定义了父类中的某个方法abstract
标示该方法为抽象方法,该方法不用撰写{}区块,直接用“;”结束即可,表示这个类不完整,不完整的类不能产生实例子类如果继承抽象类,两种方法:一种是在子类中表示该方法为
abstract
,另一种方法就是操作抽象方法6.2 继承语法细节
- Java中所有对象都是一种
java.lang.object
等细节 protected
用在变量被定义为private
的同时还想让子类直接存取时
public abstract class Role { protected String name; protected int level; protected int blood; ...}
- 被声明为
protected
的成员在相同包的类可以直接存取,不同包的类可以在继承后的子类直接存取 - 利用
toString()
方法返回字符串描述
public class SwordsMan extends Role{ public void fight(){ System.out.println("挥剑攻击"); } public String toString(){ return String.format("剑士(%s,%d,%d)", this.name,this.level,this.blood); }}
public class Magician extends Role{ public void fight(){ System.out.println("魔法攻击");} public String toString(){ return String.format("魔法师(%s,%d,%d)", this.name,this.level,this.blood); }}
public
:类内部可存取,相同包类可存取,不同包类可存取protected
:类内部可存取,相同包类可存取,子类可存取无
:类内部可存取,相同包类可存取,不同包类不可存取private
:类内部可存取,相同包类不可存取,不同包类不可存取- 想取得父类中的方法定义,可以在调用方法前,加上
super
关键字 - 可以使用
super
调用父类方法,不能定义为private
(这就只限定在类内使用) - 重新定义方法,子类对于父类中的方法权限,只能扩大不能缩小
public abstract class Role { ... public String toString(){ return String.format("(%s,%d,%d)",this.name,this.level,this.blood); }}
public class SwordsMan extends Role{ ... @Override public String toString(){ return "剑士" + super.toString(); }}
public class Magician extends Role{ ... @Override public String toString(){ return "魔法师" + super.toString(); }}
- 如果类有继承关系,在创建子类实例后,会先进行父类定义的初始流程,再进行子类中定义的初始流程
- 父类中可以重载多个构造函数,如果子类中没有指定执行哪个构造函数,默认会调用父类中无参数构造函数
- 用
super()
指定想执行的父类中的构造函数super()
和this()
只能择一调用,而且要在构造函数第一行执行class
前使用final
关键字定义,表示这个类是最后一个了,不会再有子类,不能被继承- 方法也可以被限定为
final
,表示最后一次定义方法,子类不可以再重新定义该方法
- java中任何类追溯至最上层父类,一定就是
java.lang.Object
,所有对象一定是一种Object
,任何类型的对象,都可以使用Object
的名称来参考- 使用数组类收集各种对象,类型声明为
Object[]
- 使用数组类收集各种对象,类型声明为
import java.util.Arrays;public class ArrayList { private Object[] list; private int next;//下一个可存储对象的索引 public ArrayList(int capacity){ list = new Object[capacity]; } public ArrayList(){ this(16); } public void add(Object o){ if(next == list.length){ list = Arrays.copyOf(list,list.length*2); } list[next++] = o; } public Object get(int index){ return list [index]; } public int size(){ return next; }}
import java.util.Scanner;import static java.lang.System.out;public class Guest { public static void main(String[] args){ ArrayList names = new ArrayList(); collectNameTo(names); out.println("访客名单:"); printUpperCase(names); } static void collectNameTo(ArrayList names){ Scanner console = new Scanner(System.in); while(true){ out.print("访客名称:"); String name = console.nextLine(); if(name.equals("quit")){ break; } names.add(name); } } static void printUpperCase(ArrayList names){ for(int i = 0;i<= names.size();i++) { String name = (String) names.get(i); out.println(name.toUpperCase()); } }}
运行截图
- 重新定义
toString()
、equals
- 没有重新定义
equals
,使用时方法等同于==
instanceof
用来判断对象是否由某个类创建,左操作数是对象,右操作数是类(左操作数类型是右操作数类型的子类型也返回true)
- 没有重新定义
- 垃圾收集
- 程序流程中已经无法使用的某个对象就成为了垃圾
- JVM有垃圾收集机制(GC)
- 通过主程线不能参考到的对象,形同孤岛的对象会被GC处理掉
public abstract class GuessGame { public void go(){ int number = (int)(Math.random() * 10); int guess; do{ print("输入数字:"); guess = nextInt(); }while(guess != number); println("猜中了"); } public void println(String text){ print(text + "\n"); } public abstract void print(String text); public abstract int nextInt();}
import java.util.Scanner;public class ConsoleGame extends GuessGame{ private Scanner scanner = new Scanner(System.in); @Override public void print(String text){ System.out.print(text); } @Override public void println(String text){ System.out.println(text); } @Override public int nextInt(){ return scanner.nextInt(); }}
public class Guess { public static void main(String[] args){ GuessGame game = new ConsoleGame(); game.go(); }}
- 运行截图
第七章 接口与多态
7.1 何为接口
- 优先考虑接口而不是继承
- 用
interface
定义行为,表示某个行为可以被所有东西所有- 接口可以用于行为定义但不定义操作
public interface Swimmer { public abstract void swim();}
- 类要操作接口,必须使用
implements
关键字,操作某接口时,对接口中定义的方法有两种处理方式,一是操作接口中定义的方法,二是再度将该方法标示为abstract
public abstract class Fish implements Swimmer{ protected String name; public Fish (String name){ this.name = name; } public String getName(){ return name; } @Override public abstract void swim();}
public class Human implements Swimmer { private String name; public Human(String name){ this.name = name; } public String getName(){ return name; } @Override public void swim(){ System.out.printf("人类 %s 潜行 %n",name); }}
public class Submarine implements Swimmer{ private String name; public Submarine(String name){ this.name = name; } public String getName(){ return name; } @Override public void swim(){ System.out.printf("潜水艇 %s 潜行 %n",name); }}
- 继承会有“是一种”关系,操作接口则表示“拥有行为”
- 使用接口定义行为编译程序,判断方式是“右边是不是拥有左边的行为”或“右边是不是操作了左边接口”
- 加入
cast
语法
public class Ocean { public static void main(String[] args){ doSwim(new Anemonefish("尼莫")); doSwim(new Shark("兰尼")); doSwim(new Human("贾斯汀")); doSwim(new Submarine("黄色一号")); } static void doSwim(Swimmer swimmer){ swimmer.swim(); }}
- 运行截图
public class Seaplane implements Swimmer,Flyer { private String name; public Seaplane(String name){ this.name = name; } @Override public void fly(){ System.out.printf("海上飞机 %s 在飞 %n",name); } @Override public void swim(){ System.out.printf("海上飞机 %s 航行海面 %n",name); }}
- 类可以操作两个以上的类,拥有两种以上的行为
- 类可以同时继承某个类并操作某些接口
public class FlyingFish extends Fish implements Flyer { public FlyingFish(String name){ super(name); } @Override public void swim(){ System.out.println("飞鱼游泳"); } @Override public void fly(){ System.out.println("飞鱼会飞"); }}
7.2 接口语法细节
- 可使用
interface
来定义抽象的行为与外观- 接口中无方法操作时,一定是公开且抽象的,如:
public innterface Swimmer
- 接口中的方法可声明为
public abstract
- 可以省略方法前的
public abstract
,编译程序会自动加上
- 接口中无方法操作时,一定是公开且抽象的,如:
- 枚举常数:接口中定义常数
- 在
interface
中只能定义public static final
的枚举常数 public static final
可省略,程序会自动加上- 一定要使用
=
指定值
- 在
public interface Action { public static final int STOP = 0; public static final int Right = 1; public static final int Left = 2; public static final int UP = 3; public static final int DOWN = 4;}
import static java.lang.System.out;public class Game { public static void main(String[] args){ play(1); play(3); } public static void play(int action){ switch(action){ case 0: out.print("播放停止动画"); break; case 1: out.print("播放向右动画"); break; case 2: out.print("播放向左动画"); break; case 3: out.print("播放向上动画"); break; case 4: out.print("播放向下动画"); break; default: out.print("不支持此动作"); break; } }}
运行截图
- 类如果操作两个接口都定义了某方法
- 程序会通过编译
- 如果表示不同行为,名称上应该有所不同
- 相同行为可以定义一个父接口,两个类继承该接口
- 接口可以继承别的接口,也可以继承两个以上接口
- 临时继承某个类或操作某个接口并建立实例,子类和接口只使用一次,不需要为他们定义名称
- 匿名内部类语法:
new 父类()|接口(){//类文本操作}
- 匿名内部类
- 继承Object类重新定义toString方法,操作借口同理
Object = new Object();//定义一个没有名称的类,继承Object类并实例化这个没有名称的类{//继承Object并重新定义toString()方法@Overridepublic String toString(){ return”无聊的语法示范";}}
enum
语法用于定义枚举常数enum
定义了特殊的类,继承java.lang.Enum
- 直接撰写继承
Enum
类会出错
public enum Action { STOP,RIGHT,LEFT,UP,DOWN;}
教材学习中的问题和解决过程
- 问题:对于加代码中各种关键字不理解导致看不懂代码
解决过程:很多关键词都是看书然后敲一遍代码就带过了,导致记忆不深刻,所以还要回头再看一遍,加深记忆
- 问题:对于
protected
、private
、public
和无修饰之间的的关系 解决过程:看了书上的表格,
public
、protected
、无
、private
,是按照范围从大到小排列的
代码调试中的问题和解决过程
- 问题:p207代码Submarine无法操作Diver接口
public class Diver extends Swimmer { public abstract void dive();}
- 解决过程:检查代码发现把
interface
关键字写成了class
,让接口变成了一个类
public interface Diver extends Swimmer { public abstract void dive();}
- 问题:OceanWorld4代码Ocean.java无法执行
public class Ocean { public static void main(String[] args){ doSwim(new Human("贾斯汀")); doSwim(new Submarine("黄色一号")); doSwim(new Seaplane("空军零号")); doSwim(new FlyingFish("甚平")); } static void doSwim(Swimmer swimmer){ swimmer.swim(); }}
- 解决过程:IDEA显示第3行代码有误,发现
doSwim()
方法是对Swimmer
类型的对象建立的方法,而OceanWorld4中Human已经不全部是Swimmer
类型,不操作Swimmer
接口,无法使用该方法,将Human
改为其操作Swimmer
接口的子类SwimPlayer
即可
public class Ocean { public static void main(String[] args){ doSwim(new SwimPlayer("贾斯汀")); doSwim(new Submarine("黄色一号")); doSwim(new Seaplane("空军零号")); doSwim(new FlyingFish("甚平")); } static void doSwim(Swimmer swimmer){ swimmer.swim(); }}
其他(感悟、思考等,可选)
- 学习的内容越来越难了,体会到不亲自敲代码真的会不能理解好多内容,在调试代码中出错并且进行改正也是对每个知识点加深理解的过程
- 看了以后几周的学习任务,内容只增不减,需要更加效率高的学习
- 这周的学习让我体会到java语言功能的多样,操作接口、继承类,有时相同的问题可以有不同的方法解决,就需要多学习,选择效率高、准确度高的方法来解决问题
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 30/30 | 1/2 | 16/16 | 基本知识 |
第二周 | 100/130 | 1/3 | 18/38 | 基本语法 |
第三周 | 100/230 | 1/4 | 22/60 | 对象封装 |
第四周 | 300/530 | 1/5 | 30/90 | 继承接口 |