1 对象
1.1 成员变量
- 成员变量在定义时会自动初始化
- 定义初始化可以调用函数,还可以使用已经定义的成员变量
1.2 构造函数
- 函数名与类名相同
- 在创建对象时,系统会自动调用构造函数
- 该函数不能有返回值
1.3 函数重载
- 一个类可以有多个构造函数(参数表不同)
- 创建对象时给出不同的参数值,会自动调用不同的构造函数
- 通过this()可以调用其他构造函数
1.4 对象交互实例:时钟
public class Display {
private int value = 0; //值
private int limit = 0; //上限
public Display(int limit){
this.limit = limit;
}
public void increase() {
value++;
if(value == limit) {
value = 0;
}
}
public int getValue() {
return value;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Display h = new Display(24);
while(true) {
h.increase();
System.out.println(h.getValue());
}
}
}
public class Clock {
private Display hour = new Display(24);
private Display minute = new Display(60);
private Display second = new Display(60);
public void start() {
while(true){
second.increase(); //读秒
if(second.getValue() == 0) {
minute.increase(); //秒数满60,分钟数+1
if(minute.getValue() == 0) {
hour.increase(); //分钟数满60,小时数+1
}
}
System.out.printf("%02d:%02d:%02d\n", hour.getValue(), minute.getValue(), second.getValue());
}
}
public static void main(String[] args) {
Clock clock = new Clock();
clock.start();
}
}
2 类
2.1 类变量(静态变量,带static关键字)
- 可以通过类名或对象名进行访问
- 一个对象修改了static变量,所有对象的static变量都会改变(即static变量属于类,系统只保留一份,为所有对象共用)
2.2 类函数(静态函数,带static关键字)
- 可以通过类名或对象名进行访问
- 类函数是属于类的,不属于某个具体的对象
- static函数只能访问static函数或static变量
3 顺序容器ArrayList<>
3.1 ArrayList<>
3.2 实例:记事本
3.2.1 记事本功能
3.2.2 接口设计
3.2.3 代码
public class NoteBook {
private ArrayList<String> notes = new ArrayList<>();
//添加字符串
public void add(String s) {
notes.add(s);
}
//在location前面添加字符串
public void add(String s, int location) {
notes.add(location, s);
}
//获取字符串的数量
public int getSize() {
return notes.size();
}
//获取第index个字符串
public String getNote(int index) {
return notes.get(index);
}
//删除第index个字符串
public void removeNote(int index) {
notes.remove(index);
}
//获取所有字符串
public String[] list() {
String[] a = new String[notes.size()];
// for(int i=0; i<notes.size(); i++) {
// a[i] = notes.get(i);
// }
notes.toArray(a); //将容器的内容传递到数组中
return a;
}
public static void main(String[] args) {
NoteBook nb = new NoteBook();
nb.add("first");
nb.add("second");
nb.add("third", 1);
System.out.println(nb.getSize());
System.out.println(nb.getNote(0));
nb.removeNote(1);
String[] a = nb.list();
for(String s : a) {
System.out.println(s);
}
}
}
4 集合容器HashSet
public static void main(String[] args) {
HashSet<String> hs = new HashSet<>();
hs.add("2");
hs.add("1");
hs.add("1");
System.out.println(hs);
}
//[1, 2]
总结:集合中的元素是无序且唯一的
5 HashMap
- HashMap的元素是键值对,键是唯一的
import java.util.HashMap;
import java.util.Scanner;
public class Coin {
private HashMap<Integer, String> coinnames = new HashMap<>();
public Coin() {
coinnames.put(1, "penny");
coinnames.put(10, "dime");
coinnames.put(25, "quarter");
coinnames.put(50, "half-dollar");
}
public String getName(int amount) {
if(coinnames.containsKey(amount))
return coinnames.get(amount);
else
return "NOT FOUND!";
}
public void printNames() {
//通过键遍历所有的值(foreach循环)
for(Integer k : coinnames.keySet()) {
System.out.println(coinnames.get(k));
}
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("输入美元的一个面值(1、10、25、50):");
int amount = in.nextInt();
Coin coin = new Coin();
String name = coin.getName(amount);
System.out.println(name);
System.out.println("=====================");
coin.printNames();
}
}
6 继承
6.1 资料库1.0(未使用继承)
- Database类
import java.util.ArrayList;
public class Database {
private ArrayList<CD> listCD = new ArrayList<CD>();
private ArrayList<DVD> listDVD = new ArrayList<DVD>();
public void add(CD cd) {
listCD.add(cd);
}
public void add(DVD dvd) {
listDVD.add(dvd);
}
public void list() {
for(CD cd : listCD) {
cd.print();
}
for(DVD dvd : listDVD) {
dvd.print();
}
}
public static void main(String[] args) {
Database db = new Database();
db.add(new CD("celebrity", "IU", 1000, 180, "perfect"));
db.add(new CD("Good day", "IU", 1000, 180, "NO.1"));
db.add(new DVD("我的大叔", "Mr.金", 100, "豆瓣评分9.6"));
db.list();
}
}
- CD类
public class CD {
private String title;
private String artist;
private int numofTracks;
private int playingTime;
private boolean gotIt;
private String comment;
public CD(String title, String artist, int numofTracks, int playingTime, String comment) {
// super();
this.title = title;
this.artist = artist;
this.numofTracks = numofTracks;
this.playingTime = playingTime;
this.comment = comment;
}
public void print() {
System.out.println("CD:"+title+":"+artist);
}
}
- DVD类
public class DVD {
private String title;
private String director;
private int playingTime;
private boolean gotIt;
private String comment;
public DVD(String title, String director, int playingTime, String comment) {
super();
this.title = title;
this.director = director;
this.playingTime = playingTime;
this.comment = comment;
}
public void print() {
System.out.println("DVD:"+title+":"+director);
}
}
PS:对比发现CD类和DVD类的成员变量和方法高度相似,代码非常的臃肿(恺哥说这是代码质量差的表现,而且不具有可扩展性,不利于代码维护),因此很有必要引入“继承”的概念,可以将相似的部分设计成一个父类,然后让CD类和DVD类继承这个父类。
6.2 资料库2.0(使用继承)
- Database类
import java.util.ArrayList;
public class Database {
// private ArrayList<CD> listCD = new ArrayList<CD>();
// private ArrayList<DVD> listDVD = new ArrayList<DVD>();
private ArrayList<Item> listItem = new ArrayList<>();
// public void add(CD cd) {
// listCD.add(cd);
// }
//
// public void add(DVD dvd) {
// listDVD.add(dvd);
// }
public void add(Item item) {
listItem.add(item);
}
public void list() {
// for(CD cd : listCD) {
// cd.print();
// }
// for(DVD dvd : listDVD) {
// dvd.print();
// }
for(Item item : listItem) {
item.print();
}
}
public static void main(String[] args) {
Database db = new Database();
db.add(new CD("celebrity", "IU", 1000, 180, "perfect"));
db.add(new CD("Good day", "IU", 1000, 180, "NO.1"));
db.add(new DVD("我的大叔", "Mr.金", 100, "豆瓣评分9.6"));
db.list();
}
}
- Item父类
public class Item {
private String title;
private int playingTime;
private boolean gotIt;
private String comment;
public Item(String title, int playingTime, boolean gotIt, String comment) {
super();
this.title = title;
this.playingTime = playingTime;
this.gotIt = gotIt;
this.comment = comment;
}
public void print() {
System.out.print(title);
}
}
- CD类
public class CD extends Item{
private String artist;
private int numofTracks;
public CD(String title, String artist, int numofTracks, int playingTime, String comment) {
super(title, playingTime, false, comment);
// this.title = title;
this.artist = artist;
this.numofTracks = numofTracks;
// this.playingTime = playingTime;
// this.comment = comment;
}
public void print() {
System.out.print("CD:");
super.print();
System.out.println(":" + artist);
}
}
- DVD类
public class DVD extends Item{
private String director;
public DVD(String title, String director, int playingTime, String comment) {
super(title, playingTime, false, comment);
// this.title = title;
this.director = director;
// this.playingTime = playingTime;
// this.comment = comment;
}
public void print() {
System.out.print("DVD:");
super.print();
System.out.println(":" + director);
}
}
7 多态变量与向上造型
7.1 多态变量
在上面的例子中,item变量的声明类型是Item,但动态类型可能是CD,也可能是DVD,因此item是一个多态变量。
7.2 向上造型
7.3 函数调用的绑定
7.4 方法覆盖(重载)
8 Object类
8.1 toString()方法
- 可以返回一个代表该对象的字符串,如果有特殊要求,需要进行重载(可利用eclipse自动生成)。
public class CD extends Item{
private String artist;
private int numofTracks;
public CD(String title, String artist, int numofTracks, int playingTime, String comment) {
super(title, playingTime, false, comment);
this.artist = artist;
this.numofTracks = numofTracks;
}
@Override
public String toString() {
return "CD [artist=" + artist + ", numofTracks=" + numofTracks + "]";
}
public static void main(String[] args) {
CD cd = new CD("abd", "cc", 0, 0, "good");
System.out.print(cd.toString());
}
}
//CD [artist=cc, numofTracks=0]
8.2 equals()方法
- 可以比较两个对象的内容是否相等,如果是自定义的类对象,需要进行重载。
public class CD extends Item{
private String artist;
private int numofTracks;
public CD(String title, String artist, int numofTracks, int playingTime, String comment) {
super(title, playingTime, false, comment);
this.artist = artist;
this.numofTracks = numofTracks;
}
@Override
public boolean equals(Object obj) {
CD cd = (CD)obj;
return artist.equals(cd.artist);
}
public static void main(String[] args) {
CD cd = new CD("abd", "cc", 0, 0, "good");
CD cd1 = new CD("abd", "cc", 0, 0, "good");
System.out.println(cd.equals(cd1));
}
}
//true
9 城堡游戏(需要解决存在的代码问题)
- Room类
public class Room {
public String description;
public Room northExit;
public Room southExit;
public Room eastExit;
public Room westExit;
public Room(String description)
{
this.description = description;
}
public void setExits(Room north, Room east, Room south, Room west)
{
if(north != null)
northExit = north;
if(east != null)
eastExit = east;
if(south != null)
southExit = south;
if(west != null)
westExit = west;
}
@Override
public String toString()
{
return description;
}
}
- Game类
import java.util.Scanner;
public class Game {
private Room currentRoom;
public Game()
{
createRooms();
}
private void createRooms()
{
Room outside, lobby, pub, study, bedroom;
// 制造房间
outside = new Room("城堡外");
lobby = new Room("大堂");
pub = new Room("小酒吧");
study = new Room("书房");
bedroom = new Room("卧室");
// 初始化房间的出口
outside.setExits(null, lobby, study, pub);
lobby.setExits(null, null, null, outside);
pub.setExits(null, outside, null, null);
study.setExits(outside, bedroom, null, null);
bedroom.setExits(null, null, null, study);
currentRoom = outside; // 从城堡门外开始
}
private void printWelcome() {
System.out.println();
System.out.println("欢迎来到城堡!");
System.out.println("这是一个超级无聊的游戏。");
System.out.println("如果需要帮助,请输入 'help' 。");
System.out.println();
System.out.println("现在你在" + currentRoom);
System.out.print("出口有:");
if(currentRoom.northExit != null)
System.out.print("north ");
if(currentRoom.eastExit != null)
System.out.print("east ");
if(currentRoom.southExit != null)
System.out.print("south ");
if(currentRoom.westExit != null)
System.out.print("west ");
System.out.println();
}
// 以下为用户命令
private void printHelp()
{
System.out.print("迷路了吗?你可以做的命令有:go bye help");
System.out.println("如:\tgo east");
}
private void goRoom(String direction)
{
Room nextRoom = null;
if(direction.equals("north")) {
nextRoom = currentRoom.northExit;
}
if(direction.equals("east")) {
nextRoom = currentRoom.eastExit;
}
if(direction.equals("south")) {
nextRoom = currentRoom.southExit;
}
if(direction.equals("west")) {
nextRoom = currentRoom.westExit;
}
if (nextRoom == null) {
System.out.println("那里没有门!");
}
else {
currentRoom = nextRoom;
System.out.println("你在" + currentRoom);
System.out.print("出口有: ");
if(currentRoom.northExit != null)
System.out.print("north ");
if(currentRoom.eastExit != null)
System.out.print("east ");
if(currentRoom.southExit != null)
System.out.print("south ");
if(currentRoom.westExit != null)
System.out.print("west ");
System.out.println();
}
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
Game game = new Game();
game.printWelcome();
while ( true ) {
String line = in.nextLine();
String[] words = line.split(" ");
if ( words[0].equals("help") ) {
game.printHelp();
} else if (words[0].equals("go") ) {
game.goRoom(words[1]);
} else if ( words[0].equals("bye") ) {
break;
}
}
System.out.println("感谢您的光临。再见!");
in.close();
}
}
9.1 消除代码复制
- 程序中存在相似甚至相同的代码块,是非常低级的代码质量问题。
- 消除代码复制的两个基本手段:封装成函数和抽象出父类。
以下代码出现了两次:
System.out.println("你在" + currentRoom);
System.out.print("出口有: ");
if(currentRoom.northExit != null)
System.out.print("north ");
if(currentRoom.eastExit != null)
System.out.print("east ");
if(currentRoom.southExit != null)
System.out.print("south ");
if(currentRoom.westExit != null)
System.out.print("west ");
System.out.println();
可以封装成函数:
public void showPrompt() {
System.out.println("现在你在" + currentRoom);
System.out.print("出口有:");
if(currentRoom.northExit != null)
System.out.print("north ");
if(currentRoom.eastExit != null)
System.out.print("east ");
if(currentRoom.southExit != null)
System.out.print("south ");
if(currentRoom.westExit != null)
System.out.print("west ");
System.out.println();
}
9.2 封装性
- 可以用封装来降低耦合
9.2.1 成员变量的私有性(大多数情况下)
Room类的成员变量是public的,可以在Game类中任意访问:
public String description;
public Room northExit;
public Room southExit;
public Room eastExit;
public Room westExit;
修改为private:
private String description;
private Room northExit;
private Room southExit;
private Room eastExit;
private Room westExit;
9.2.2 类之间要低耦合
- 耦合:指的是类和类之间的联系。要努力确保类之间是低耦合关系。耦合度决定了修改应用程序的容易程度,在一个低耦合的系统中,常常可以修改一个类,但同时不会修改其他类,而且整个程序还可以正常运作。也就是说,低耦合的系统便于维护。
Game类的showPrompt()方法大量使用了Room类的成员变量
if(currentRoom.northExit != null)
System.out.print("north ");
if(currentRoom.eastExit != null)
System.out.print("east ");
if(currentRoom.southExit != null)
System.out.print("south ");
if(currentRoom.westExit != null)
System.out.print("west ");
修改为让Room类访问自己的成员变量,做自己原本该做的事
public String getExitDesc() {
StringBuffer sb = new StringBuffer();
if(northExit != null)
sb.append("north ");
if(eastExit != null)
sb.append("east ");
if(southExit != null)
sb.append("south ");
if(westExit != null)
sb.append("west ");
return sb.toString();
}
Game类的goRoom()方法大量使用了Room类的成员变量
if(direction.equals("north")) {
nextRoom = currentRoom.northExit;
}
if(direction.equals("east")) {
nextRoom = currentRoom.eastExit;
}
if(direction.equals("south")) {
nextRoom = currentRoom.southExit;
}
if(direction.equals("west")) {
nextRoom = currentRoom.westExit;
}
修改为让Room类访问自己的成员变量,做自己原本该做的事
public Room getExit(String direction) {
Room ret = null;
if(direction.equals("north")) {
ret = northExit;
}
if(direction.equals("east")) {
ret = eastExit;
}
if(direction.equals("south")) {
ret = southExit;
}
if(direction.equals("west")) {
ret = westExit;
}
return ret;
}
- 用接口来实现聚合
- 聚合与程序中一个单独的单元所承担的任务的数量和种类有关,它是针对类或方法这样大小的程序单元而言的。理想情况下,一个代码单元应该负责一个聚合的任务。一个方法应该实现一个逻辑操作,而一个类应该代表一定类型的实体。
9.3 可扩展性
- 可扩展性:代码的某些部分不需要经过修改就能适应将来可能的变化。
9.3.1 用容器来实现灵活性
Room类的方向成员变量:
private Room northExit;
private Room southExit;
private Room eastExit;
private Room westExit;
修改为:
private HashMap<String, Room> exits = new HashMap<>();
9.3.2 以框架+数据来提高可扩展性
- 从程序中识别出框架和数据,以代码实现框架,将部分功能以数据的方式加载,这样能在很大程度上实现可扩展性。
9.4 最终代码
- Game类
import java.util.HashMap;
import java.util.Scanner;
public class Game {
private Room currentRoom;
private HashMap<String, Handler> handlers = new HashMap<>();
public Game()
{
handlers.put("bye", new HandlerBye(this));
handlers.put("help", new HandlerHelp(this));
handlers.put("go", new HandlerGo(this));
createRooms();
}
private void createRooms()
{
Room outside, lobby, pub, study, bedroom;
// 制造房间
outside = new Room("城堡外");
lobby = new Room("大堂");
pub = new Room("小酒吧");
study = new Room("书房");
bedroom = new Room("卧室");
// 初始化房间的出口
outside.setExit("east", lobby);
outside.setExit("south", study);
outside.setExit("west", pub);
lobby.setExit("west", outside);
pub.setExit("east", outside);
study.setExit("north", outside);
study.setExit("east", bedroom);
bedroom.setExit("west", study);
// 扩展出口
lobby.setExit("up", pub);
pub.setExit("down", lobby);
currentRoom = outside; // 从城堡门外开始
}
private void printWelcome() {
System.out.println();
System.out.println("欢迎来到城堡!");
System.out.println("这是一个超级无聊的游戏。");
System.out.println("如果需要帮助,请输入 'help' 。");
System.out.println();
showPrompt();
}
public void goRoom(String direction)
{
Room nextRoom = currentRoom.getExit(direction);
if (nextRoom == null) {
System.out.println("那里没有门!");
}
else {
currentRoom = nextRoom;
showPrompt();
}
}
public void showPrompt() {
System.out.println("现在你在" + currentRoom);
System.out.print("出口有:");
System.out.print(currentRoom.getExitDesc());
System.out.println();
}
public void play() {
Scanner in = new Scanner(System.in);
while ( true ) {
String line = in.nextLine();
String[] words = line.split(" ");
Handler handler = handlers.get(words[0]);
String value = "";
if(words.length > 1)
value = words[1];
if(handler != null) {
handler.doCmd(value);
if(handler.isBye())
break;
}
}
in.close();
}
public static void main(String[] args) {
Game game = new Game();
game.printWelcome();
game.play();
System.out.println("感谢您的光临。再见!");
}
}
- Room类
import java.util.HashMap;
public class Room {
private String description;
private HashMap<String, Room> exits = new HashMap<>();
public Room(String description)
{
this.description = description;
}
public void setExit(String dir, Room room) {
exits.put(dir, room);
}
@Override
public String toString()
{
return description;
}
public Room getExit(String direction) {
return exits.get(direction);
}
public String getExitDesc() {
StringBuffer sb = new StringBuffer();
for(String dir : exits.keySet()) {
sb.append(dir);
sb.append(' ');
}
return sb.toString();
}
}
- Handler类
public class Handler {
protected Game game;
public Handler(Game game) {
this.game = game;
}
public void doCmd(String word) {}
public boolean isBye() {
return false;
}
}
- HandlerBye类
public class HandlerBye extends Handler{
public HandlerBye(Game game) {
super(game);
}
@Override
public boolean isBye() {
return true;
}
}
- HandlerHelp类
public class HandlerHelp extends Handler {
public HandlerHelp(Game game) {
super(game);
}
@Override
public void doCmd(String word) {
System.out.println("迷路了吗?你可以做的命令有:go bye help");
System.out.println("如:\tgo east");
}
}
- HandlerGo类
public class HandlerGo extends Handler {
public HandlerGo(Game game) {
super(game);
}
@Override
public void doCmd(String word) {
// TODO Auto-generated method stub
game.goRoom(word);
}
}
10 抽象与接口
10.1 抽象方法
- 抽象方法使用abstract关键字来修饰。
- 抽象方法没有方法体。
- 如果一个类有了一个抽象方法,这个类就必须声明为抽象类。
10.2 抽象类
- 抽象类用abstract关键字来修饰,其作用仅仅是表达接口,而不是具体的实现细节。
- 抽象类不能构造对象,但可以定义变量,并用继承了该抽象类的非抽象子类对象进行赋值
- 如果父类是抽象类,那么子类必须实现(覆盖)所有在父类中的抽象方法,否则子类也成为一个抽象类。
- 一个抽象类可以没有任何抽象方法,所有的方法都有方法体,但是整个类是抽象的。
10.3 数据与表现分离
实例:细胞自动机
View和Field的关系
- 责任驱动的设计
- 网格化
处理数据时只需要知道第几行第几列,而无需关心横纵坐标,这样更方便。
10.4 接口
10.4.1 接口的概念
10.4.2 实现接口
10.4.3 面向接口的编程方式
11 MVC设计模式
12 异常
- 在try语句块里放入可能出现异常的代码段
- 在catch语句块里编写异常处理语句
- catch语句块可以有多个,用于处理多个可能出现的异常
12.1 异常捕获与处理
public class ArrayIndex {
public static void f() {
int[] a = new int[10];
a[10] = 100;
System.out.println("hello");
}
public static void g() {
f();
}
public static void h() {
int i = 10;
if( i<100 ) {
g();
}
}
public static void k() {
try {
h();
}catch (NullPointerException e) {
System.out.println("k()");
}
}
public static void main(String[] args) {
try {
k();
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("caught");
System.out.println(e.getMessage());
System.out.println(e);
e.printStackTrace();
}
System.out.println("main");
}
}
//caught
//Index 10 out of bounds for length 10
//java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 10
//java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 10
// at demo/demo.ArrayIndex.f(ArrayIndex.java:6)
// at demo/demo.ArrayIndex.g(ArrayIndex.java:11)
// at demo/demo.ArrayIndex.h(ArrayIndex.java:17)
// at demo/demo.ArrayIndex.k(ArrayIndex.java:23)
// at demo/demo.ArrayIndex.main(ArrayIndex.java:31)
//main
12.2 再度抛出
- 当前层次需要进行异常处理,但不能做最终决定,此时需要将异常再度抛出,让高层进行异常处理
public class ArrayIndex {
public static void k() {
try {
int[] a = new int[10];
a[10] = 100;
System.out.println("hello");
}catch (ArrayIndexOutOfBoundsException e) {
System.out.println("k()");
throw e;
}
}
public static void main(String[] args) {
try {
k();
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("caught");
System.out.println(e.getMessage());
System.out.println(e);
e.printStackTrace();
}
System.out.println("main");
}
}
//k()
//caught
//Index 10 out of bounds for length 10
//java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 10
//java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 10
//at demo/demo.ArrayIndex.k(ArrayIndex.java:7)
//at demo/demo.ArrayIndex.main(ArrayIndex.java:17)
//main
12.3 异常声明
什么能抛出异常?