每日一狗(田园犬西瓜瓜)
主标题 文章目录- 0725(016天 多态复习、java中级-抽象类01)
- 主标题
- 1. 多态续
- 1.1 补充知识
- 1.2 类内多态
- 参数传递时的重载的最佳匹配原则
- 1.3 类间多态
- 2. 抽象类
- 2.1 基本名词概念
- 2.2 常见的问题
- 抽象类不可以被实例化?
- 什么时候使用抽象类
- 抽象类和普通类的区别
- 有构造器嘛?
- 可以不定义抽象方法吗?
- 一定是一个父类吗?
- 创建对象时,加载对象的执行顺序
- 2.3 抽象类的特点
- 2.4 模式:模板模式(待整理至考题中)
- 2.5 抽象类的作用
- 2.6 类和类之间的耦合问题
- 扩展小芝士
- 练习题
课前练习题
创建一个可扩容数组类,实现猜数游戏类
package com.test;
import java.util.Scanner;
public class 猜数游戏 {
public static void main(String[] args) {
A a = new A(20);
a.start();
a.show();
}
}
// 猜数游戏
class A {
private int v; // 要猜的数字
private MyArrays hist; // 存储猜数历史
private boolean flag = true; // 标识游戏是否正常退出
public A() {
// 默认初始化为100
this(100);
hist = new MyArrays();
}
public A(int num) {
v = num;
hist = new MyArrays();
}
public void show() {
System.out.println("============= 历史展示 =============");
hist.show();
}
public void start() {
input();
// 通过评语
if (flag) {
System.out.println("============= 游戏通关评语 ===========");
if (hist.getCount() < 5) {
System.out.println("小天才");
} else if (hist.getCount() > 20) {
System.out.println("笨蛋");
} else {
System.out.println("成功猜中");
}
}
}
public void input() {
// 开始猜数,判定大小并进行历史存储
Scanner sc = new Scanner(System.in);
int tmp;
while (true) {
System.out.println("请输入第" + (hist.getCount() + 1) + "次猜数(-1表示退出)");
tmp = sc.nextInt();
if (tmp == -1) { // 非正常退出,修改标识
System.out.println("非正常退出");
flag = false;
break;
}
hist.add(tmp);
if (tmp == v) {
System.out.println("猜中了");
break;
} else if (tmp > v) {
System.out.println("猜大了");
} else {
System.out.println("猜小了");
}
}
sc.close();
}
// =======================================
}
class MyArrays {
private int[] arr;
private int count;
private static final double GROW = 1.5; // 扩容比
public MyArrays() {
arr = new int[10];
}
public MyArrays(int length) {
arr = new int[length];
}
// 追加数据 合理扩容
public void add(int v) {
if (count >= arr.length) {
expansion();
}
arr[count++] = v;
}
// 展示存储的数组
public void show() {
for (int i = 0; i < count; i++) {
System.out.println(arr[i]);
}
}
// 扩容数组大小1.5倍
private void expansion() {
int len = (int) (arr.length * GROW);
int[] newArr = new int[len];
for (int i = 0; i < arr.length; i++) {
newArr[i] = arr[i];
}
this.arr = newArr;
}
public int getCount() {
return count;
}
}
1. 多态续
1.1 补充知识
继承的:编程是对现实世界的模拟体现。
返回值类型可以设定为一个父类,代码返回值可以返回其对应的子孙类。
静态方法:用谁声明就调用谁的方法
1.2 类内多态方法的重载
参数传递时的重载的最佳匹配原则- 方法参数类型逐渐向上找
- 还有一种就是有一个是不确定个数参数方法和无参方法的抉择是选择无参方法
package com.ketang;
public class Test01 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Test01 t1 = new Test01();
t1.pp(5); // int
}
public void pp(int i) {
System.out.println("int");
}
public void pp(Integer i) {
System.out.println("Integer");
}
public void pp(Number i) {
System.out.println("Number");
}
public void pp(Object i) {
System.out.println("Object");
}
}
1.3 类间多态
父子类,方法的继承或重写
2. 抽象类 2.1 基本名词概念
抽象方法:抽象类中只有方法声明无方法体的方法
- 有方法体连{}都不能有
- 使用abstract修饰
抽象类:类的抽象
-
没有抽象属性的概念
-
抽象类中不一定包含抽象方法,抽象方法一定在抽象类中
-
不能被new实例化对象(抽象类可以定义构造方法)
-
抽象类和类的区别就是抽象类不能new且可能会有抽象方法
-
抽象类一般都是定义规则,通用代码也可以直接在抽象类中实现
抽象类的实现类:
- 实现类必须实现抽象类的抽象方法方法
- 抽象类的抽象类,抽象类可以不对抽象类的抽象类中的抽象方法进行实现
案例:
// 抽象类
package com.chouxiang;
public abstract class Animal {
public abstract void work();
public void sleep() {
System.out.println("我在睡觉");
}
}
/ 实现类
package com.chouxiang;
public class Dog extends Animal {
@Override
public void work() {
System.out.println("旺旺在工作");
};
public static void main(String[] args) {
// TODO Auto-generated method stub
Dog d = new Dog();
d.sleep(); // 我在睡觉
d.work(); // 旺旺在工作
}
}
2.2 常见的问题
抽象类不可以被实例化?
由于抽象方法中没有任何程序,它被实例化出来没有任何价值。
抽象类中的所有的抽象方法必须被继承子类实现后,该子类才可以被实例化,否则这个子类还是抽象类
注释:抽象类中可以包含构造器、析构器、抽象方法、非抽象方法、静态方法等
什么时候使用抽象类- 当一个类的一个或多个方法为抽象方法时
- 当该类继承了一个抽象方法,但是未能完全实现父类的所有抽象方法时
- 当一个类实现一个接口,并且没有为所有抽象方法提供实现细节或方法主体时
- 抽象类不能直接实例化,并且对抽象类使用 new 运算符是编译时错误
- 抽象类允许(但不要求)抽象类包含抽象成员
- 抽象类不能被密封
- 不能被final修饰
-
有,用于给子类对象进行初始化
-
new 子类时抽象父类的构造器一定会被执行
-
可以的,但是很少见
-
目的就是不让该类创建对象
-
是的
-
因为需要子类覆盖其方法后才可以对子类实例化
- 先加载父类的静态变量和静态初始化块
- 加载子类的静态变量和静态初始化块
- 加载父类的成员变量、初始化块
- 加载父类的构造方法
- 加载子类的成员变量、初始化块
- 加载子类的构造方法
定义:方法中只有声明没有实现时,这个方法就是抽象方法,类和方法都需要被abstract修饰
- 抽象方法不能被定义为私有的,它必须要被实现类实现后才有价值
- 不能定义抽象构造函数
- 抽象类中可以定义构造器,也可以不定义构造器,使用系统默认提供的构造器,但是构造器不能定义为private的
定义抽象静态方法
- 抽象方法中可以有静态方法,但是其必须有方法体,不能是抽象方法
- 允许使用抽象类直接调用该方法
抽象类不能直接被创建,只能通过继承法的方式有子类实现对应的抽象方法
所有抽象类的子类必须实现抽象父类中的所有抽象方法或者自己也声明为抽象类
抽象类中可以有抽象方法和非抽象方法。
- 抽象类不能是 final class(不可继承的)
- 不能定义静态的抽象方法,静态方法的调用时声明类的静态方法,
- 允许抽象方法的继承
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。
**主要解决:**一些方法通用,却在每一个子类都重新写了这一方法。
优点: 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。
**缺点:**每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
实现:
// 抽象排序类
package com.chouxiang;
public abstract class BubbleSorted {
public void sorted(Object[] arr) {
for (int i = 1; i < arr.length; i++) {
for (int j = 0; j < arr.length - i; j++) {
if (bigger(arr[j], arr[j + 1])) {
Object tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
public abstract boolean bigger(Object obj1, Object obj2);
}
// 排序实现类
package com.chouxiang;
public class PigSorted extends BubbleSorted {
@Override
public boolean bigger(Object obj1, Object obj2) {
// TODO Auto-generated method stub
if (obj1 != null && obj1 instanceof Pig) {
Pig p1 = (Pig) obj1;
if (obj2 != null && obj2 instanceof Pig) {
Pig p2 = (Pig) obj2;
return p1.getWeight() > p2.getWeight();
}
}
return false;
}
}
// 测试+对象类
package com.chouxiang;
import java.util.Arrays;
public class Pig {
private double weight;
public Pig(double weight) {
this.weight = weight;
}
public String toString() {
return weight + "";
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Pig[] pigs = new Pig[10];
for (int i = 0; i < pigs.length; i++) {
pigs[i] = new Pig(Math.random() * 700 + 10);
}
PigSorted ps = new PigSorted();
ps.sorted(pigs);
System.out.println(Arrays.toString(pigs));
}
//
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
}
2.5 抽象类的作用
在面向对象方法中,抽象类主要用来进行类型隐藏。构造出一个固定的一组行为的抽象描述,但是这组 行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而这一组任意个可能的具体实现 则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,因此它可以是不允许修改的
通过从这个抽象体派生,也可扩展此模块的行为功能。为了能够实现面向对象设计的一个最核心的原则 开闭原则OCP,抽象类是其中的关键所在
抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质 上相同的具体概念的抽象
OOP的核心是面向抽象编程
- 在父类中固化算法骨架,在子类中实现特殊的方法
- 依赖抽象
OOP要求类内高内聚、类间弱耦合—客户需求变动
如何实现类和类之间松耦合---使用抽象不用具体
public class A{
private BubbleSorter bs;//PigSorter
}
扩展小芝士
-
参数传递时的重载的最佳匹配原则
-
创建型模式:单例模式(懒汉模式)
1、无名粉类
要求:三个属性:字符串面码,整形粉的分量,布尔值是否带汤
package com.ketang;
public class WuMingFen {
private String theMa;
private int quantity;
private boolean likeSoup;
// 默认构造器
public WuMingFen() {
theMa = "牛肉";
quantity = 20;
likeSoup = true;
}
// 有参构造方法
public WuMingFen(String name, int quantity, boolean likeSoup) {
this.theMa = name;
this.quantity = quantity;
this.likeSoup = likeSoup;
}
// 调用其他的构造方法
public WuMingFen(String name, int quantity) {
this(name, quantity, true);
}
public String getTheMa() {
return theMa;
}
public void setTheMa(String theMa) {
this.theMa = theMa;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public boolean isLikeSoup() {// 一般情况下获取布尔值属性时方法一般写为is属性
return likeSoup;
}
public void setLikeSoup(boolean likeSoup) {
this.likeSoup = likeSoup;
}
}
2、定义一个名为Vehicles交通工具的基类
该类中应包含String类型的成员属性brand商标和color颜色,还应包含成员方法run行驶,在控制台显示“我已经开动了” 和showInfo显示信息,在控制台显示商标和颜色,并编写构造方法初始化其成员属性。
编写Car小汽车类继承于Vehicles类,增加int型成员属性seats 座位,还应增加成员方法showCar在控制台显示小汽车的信息,并编写构造方法。
编写Truck卡车类继承于Vehicles类,增加float型成员属性load载重,还应增加成员方法showTruck在控制台显示卡车的信息,并编写构造方法。 在main方法中测试以上各类。
package com.ketang;
public class Venhicles {
public static void main(String[] args) {
Venhicles v = new Venhicles("宝马", "黄色");
v.run(); // 我已经开动了:车型宝马颜色:黄色
Car c = new Car("宝马", "黄色", 5);
c.showInfo(); // 车型宝马颜色:黄色,车类别小轿车
Truck t = new Truck("大运", "红色", 80);
t.showInfo(); // 车型大运颜色:红色,车类别卡车
}
private String brand;
private String color;
private final static String CAR_TYPE = "车";
public void showInfo() {
System.out.println("车型" + brand + "颜色:" + color + ",车类别" + CAR_TYPE);
}
public Venhicles() {
super();
}
public Venhicles(String brand, String color) {
super();
this.brand = brand;
this.color = color;
}
public void run() {
System.out.println("我已经开动了:车型" + brand + "颜色:" + color);
}
public String getBrand() {
return brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
//============================================
package com.ketang;
class Car extends Venhicles {
private int seats;
private static final String CAR_TYPE = "小轿车";
public void showInfo() {
System.out.println("车型" + getBrand() + "颜色:" + getColor() + ",车类别" + CAR_TYPE);
}
public Car(String brand, String color, int seats) {
super(brand, color);
this.seats = seats;
}
}
//============================================
package com.ketang;
public class Truck extends Venhicles {
private float load;
private static final String CAR_TYPE = "卡车";
public Truck(String brand, String color, float load) {
super(brand, color);
this.load = load;
}
public void showInfo() {
System.out.println("车型" + getBrand() + "颜色:" + getColor() + ",车类别" + CAR_TYPE);
}
}
5、实现汽车类
建立一个汽车类,包括轮胎个数、汽车颜色、车身重量等属性。并通过不同的构造方法创建实例。至少要求汽车能够加速、减速、停车。要求命名规范、代码体现层次、有友好的操作提示。
package com.kehouxiti;
public class Car {
public static void main(String[] args) {
// TODO Auto-generated method stub
Car c = new Car(4, "red", 4000);
c.show(); // 车轮数:4,颜色:red,重量:4000KG
c.speedUp(); // 您踩了一下油门,加速成功!
c.speedDown();// 您降低了车速,注意安全行驶!
c.park(); // 您的爱车以停入车库!
}
private int tyreNum;
private String color;
private int weight; // KG
public void show() {
System.out.println("车轮数:" + tyreNum + ",颜色:" + color + ",重量:" + weight + "KG");
}
public Car(int tyreNum, String color, int weight) {
this.color = color;
this.tyreNum = tyreNum;
this.weight = weight;
}
public void park() {
System.out.println("您的爱车以停入车库!");
}
public void speedUp() {
System.out.println("您踩了一下油门,加速成功!");
}
public void speedDown() {
System.out.println("您降低了车速,注意安全行驶!");
}
// ====================
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public int getTyreNum() {
return tyreNum;
}
}
6、特殊类
创建一个类,为该类定义三个构造函数分别执行下列操作
1、传递两个整数值并找出其中较大的一个值
2、传递三个double值并求出其乘积
3、传递两个字符串值并检查其是否相同
4、在main方法中测试构造函数的调用
package com.kehouxiti;
public class XiTi06 {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(XiTi06.max(1, 5)); // 5
System.out.println(XiTi06.product(0.5, 2.0, 3.0)); // 3.0
System.out.println(XiTi06.stringEquals("000", "0000")); // false
}
// 3、传递两个字符串值并检查其是否相同
public static boolean stringEquals(String s1, String s2) {
boolean res;
if (s1 == null || s2 == null) {
res = false;
} else if (s1.equals(s2)) {
res = true;
} else {
res = false;
}
return res;
}
// 传递三个double值并求出其乘积
public static double product(Double i, Double j, Double k) {
double res;
if (i.equals(0.) || j.equals(0.) || k.equals(0.)) {
res = 0;
} else {
res = i * j * k;
}
return res;
}
// 传递两个整数值并找出其中较大的一个值
public static int max(int i, int j) {
return (i > j ? i : j);
}
}
7、派生类练习
7、创建一个名称为main.package的包,使它包含MainClass和MainSubClass类。MainClass类应当包含变量声明,其值从构造函数中输入。MainSubClass类从MainClass派生而来。
试执行下列操作:创建一个名称为SamePackage的类,使它导入上述包,并创建一个MainSubClass类的对象。
无运行结果
8、创建一个父类和子类。父类有一个数据成员,子类继承父类的该数据成员,通过构造函数初始化并显示该数据成员的值。
package com.kehouxiti;
public class Test08 {
public static void main(String[] args) {
// TODO Auto-generated method stub
B b = new B(20);
System.out.println(b.getAge()); // 20
}
}
class B extends A {
public B(int age) {
super(age);
}
}
class A {
private int age;
public A() {
super();
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public A(int age) {
super();
this.age = age;
}
}
9、建立一个汽车Auto类,包括轮胎个数、汽车颜色、车身重量、速度等成员变量。并通过不同的构造方法创建实例。至少要求:汽车能够加速、减速、停车。 再定义一个小汽车类Car 继承Auto 并添加空调、CD等成员变量,覆盖加速、减速的方法
package com.kehouxiti;
public class Auto {
private int tyreNum;
private String color;
private int weight; // KG
private int speed;
public void show() {
System.out.println(toString());
}
public void speedUp(int speed) {
this.speed += speed;
show();
}
public String toString() {
return "轮胎数=" + tyreNum + ", 颜色=" + color + ", 重量=" + weight + ", 速度=" + speed + "]";
}
public void speedDown(int speed) {
this.speed -= speed;
show();
}
public void park() {
this.speed = 0;
show();
}
//========================
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public int getTyreNum() {
return tyreNum;
}
public String getColor() {
return color;
}
public Auto() {
}
public Auto(int tyreNum, String color, int weight, int speed) {
super();
this.tyreNum = tyreNum;
this.color = color;
this.weight = weight;
this.speed = speed;
}
}
//======================== 实现类
package com.kehouxiti;
public class Car2 extends Auto {
public static void main(String[] args) {
Car2 c = new Car2(4, "red", 4000, 30, "LCP", "暂无");
c.show(); // 轮胎数=4, 颜色=red, 重量=4000, 速度=30, 空调=LCP, 正在播放=暂无
c.speedUp(20); // 轮胎数=4, 颜色=red, 重量=4000, 速度=50, 空调=LCP, 正在播放=暂无
}
private String createCold; // 空调牌子
private String CD;
public Car2(int tyreNum, String color, int weight, int speed, String createCold, String CD) {
super(tyreNum, color, weight, speed);
this.CD = CD;
this.createCold = createCold;
}
public void speedUp(int speed) {
setSpeed(getSpeed() + speed);
show();
}
public void speedDown(int speed) {
if (getSpeed() - speed < 0) {
setSpeed(0);
} else {
setSpeed(getSpeed() - speed);
}
show();
}
@Override
public void show() {
String res = "轮胎数=" + getTyreNum() + ", 颜色=" + getColor() + ", 重量=" + getWeight() + ", 速度=" + getSpeed()
+ ", 空调=" + createCold + ", 正在播放=" + CD;
System.out.println(res);
}
// ================
public String getCreateCold() {
return createCold;
}
public String getCD() {
return CD;
}
}
3、定义一个网络用户类,要处理的信息有用户ID、用户密码、email地址。在建立类的实例时把以上三个信息都作为构造函数的参数输入,其中用户ID和用户密码时必须的,缺省的email地址是用户ID加上字符串”@yan.com"
package com.kehouxiti;
public class UserInfo {
public static void main(String[] args) {
// TODO Auto-generated method stub
UserInfo userInfo1 = new UserInfo(123, 123);
System.out.println(userInfo1.toString()); // UserInfo [ID=123, pw=123, email=123@yan.com]
UserInfo userInfo2 = new UserInfo(456, 456, "789");
System.out.println(userInfo2.toString()); // UserInfo [ID=456, pw=456, email=789]
}
private int ID;
private int pw;
private String email;
public UserInfo(int ID, int pw) {
this(ID, pw, ID + "@yan.com");
}
// =====================
public int getPw() {
return pw;
}
@Override
public String toString() {
return "UserInfo [ID=" + ID + ", pw=" + pw + ", email=" + email + "]";
}
public UserInfo(int iD, int pw, String email) {
super();
ID = iD;
this.pw = pw;
this.email = email;
}
public void setPw(int pw) {
this.pw = pw;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getID() {
return ID;
}
}
4、编写Addition类,该类中应包含一组实现两数相加运算的重载方法。
package com.kehouxiti;
public class Addition {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(add(1, 5)); // 6
System.out.println(add(1.2, 2.5)); // 3.7
}
public static int add(int i, int j) {
return i + j;
}
public static double add(double i, double j) {
return i + j;
}
}



