目录
实验1
创建一个用表示复数的 Complex 类类型,当创建完这个类以后,就可以直接使用这个数据类型完成需要复数参与的各种算法中,图1是对创建的 Complex类型的具体说明。
该题需要完成的任务如下:
任务 1:编写 Complex 类类型。
任务 2:编写一个测试程序(TestComplex),要求对 Complex 类型中的每一个公共实例方法进行测试。
任务 3:编写一个使用 Complex 的简易客户端程序(ComplexApp),该程序完成提示用户输入两个复数,之后按照如下输出样式完成对这两个复数完成操作的相关调用。
在类里面两个private double分别储存complex的实部和虚部。为了方便比较,还定义了一个private final double EPSILON = 1e-10常量。
在Complex类中定义相对应的两个构造函数,getter和setter方法,并重写了toString方法。通过比较差值与常量EPSILON = 1e-10来判断复数是不是只有实部或者虚部,判断两个复数是否相等。Abs方法直接平方差公式就可以得到。复数之间的加减乘除用相应的公式表示即可。
在TestComplex类中,逐一验证Complex类中所有的方法即可。
在ComplexApp类中,按照题目格式验证并输出即可。
在Complex类中:
重写的toString()方法:
@Override
public String toString() {
if (imag >= 0) return real + " +" + imag + "i";
else return real + "" + imag + "i";
}
首先判断虚部是正还是负,分别返回不一样的。
equals方法:
public boolean equals(double real, double imag) {
return ((real - this.real) <= EPSILON) && ((this.real - real) <= EPSILON) &&
((imag - this.imag) <= EPSILON) && ((this.imag - imag) <= EPSILON);
}
因为浮点数直接比较会出问题,所以设置一个叫做 EPSILON 的阈值,当之间相差值小于等于 EPSILON,则认为两个值相等。当两个数的虚部和实部都小于等于EPSILON时,我们认为这两个复数相等。
multiply方法:
public Complex multiply(Complex c2) {
Complex c3 = new Complex();
c3.setReal(real * c2.getReal() - imag * c2.getImag());
c3.setImag(real * c2.getImag() + imag * c2.getReal());
return c3;
}
套用公式表达即可。
divide方法:
public Complex divide(Complex c2) throws ArithmeticException{
Complex c3 = new Complex();
double temp = c2.getImag() * c2.getImag() + c2.getReal() * c2.getReal();
// Check if the divisor is zero
if (temp == 0) {
throw new ArithmeticException("Division by zero");
}
c3.setReal((real * c2.getReal() + imag * c2.getImag()) / temp);
c3.setImag((imag * c2.getReal() - real * c2.getImag()) / temp);
return c3;
}
这里也是套用公式,但是除数可能是0,结果会出现NaN,所以我们必须判断除数是不是0,如果是就抛出ArithmeticException异常给调用者进行处理。
在TestComplex类中:
验证除法时:
// Test the divide(Complex c2) method
try{
Complex quotient = c1.divide(c3);
System.out.println("Quotient: " + quotient);
}catch (ArithmeticException e){
System.out.println("The divisor is 0");
}
我们用try catch捕捉可能出现的错误,并给予提示。
TestComplex类的结果:
ComplexApp类的结果:
第一次在代码里正式使用异常处理,异常抛出相关的语法,复习了UML类图和JavaBean。学到了double类型比较时不能直接比较,也学到了NaN可能产生的地方,以及如何避免。
实验2
图片共有1024*1024个点,取每一个点完成着色。假设取点(col, row),首先将该坐标通过工作1进行坐标映射,将映射得到的复数代入工作2完成判定,用工作2的返回值生成一个色度的灰色(Color中red、green和blue分量都设置为返回值大小即可)。将这个颜色设置到图片对应的像素点(col, row)上。
完成如下常量c对应的朱利亚集合分形图: ①0.38-0.18i②-0.835-0.2321i③-0.7269+0.1889i 。
我希望一下生成多个图片,于是定义Picture数组和Complex数组并储存对应的多个picture和complex。
先进行坐标转换:把1024*1024的像素点转换成复平面中的点是小数,注意图片的坐标原点在左上角,而复平面的坐标点在正中央。
然后判断迭代次数:当迭代中如发现 zi 的幅值 > 2,那么就不用再去迭代了,直接可以判定该值会发散,并且返回最大迭代次数,如果最大迭代次数255次依旧收敛,则返回255.
着色:遍历图片的1024*1024 个点,取每一个点完成着色。假设取点(col, row),首先
将该坐标通过工作 1 进行坐标映射,将映射得到的复数代入工作2完成判定,并设置灰度值为255-迭代次数,将这个颜色设置到图片对应的像素点(col, row)上。
坐标转化方法change:
//把图片处理的左上角为原点的坐标轴转变为复数运算的中心为原点的坐标轴xy
public static Complex change(int i, int j) {
//匹配最近的点
double x = ((double) i / 256) - 2;
double y = 2 - ((double) j / 256);
return new Complex(x, y);
}
要注意两个地方,首先是坐标轴尺度的改变,将原先的ij除以256得到新的坐标轴尺度。第二就是坐标原点和方向的变化,根据公式得到代码。
判断这个点的灰度值的函数isValid(函数名字不太正确):
public static int isValid(Complex z0, Complex c) {
Complex z = z0;
int max = 255;
for (int t = 0; t < max; t++) {
if (z.abs() > 2) return t;
z = z.multiply(z).add(c);
}
return max;
}
通过判断在哪一个循环,z的绝对值大于2来判断。将最大迭代次数返回,如果收敛则返回255,匹配图片中灰度。
定义Picture [] P和Complex [] C数组:
//定义
Picture[] P = new Picture[5];
P[0] = new Picture(1024,1024);
P[1] = new Picture(1024,1024);
P[2] = new Picture(1024,1024);
P[3] = new Picture(1024,1024);
P[4] = new Picture(1024,1024);
Complex[] C = new Complex[5];
C[0] = new Complex(-0.285, 0.156);
C[1] = new Complex(0.285, -0.01);
C[2] = new Complex(0.38, -0.18);
C[3] = new Complex(-0.835, -0.2321);
C[4] = new Complex(-0.7269, 0.1889);
具体算法实现:
for (int k = 0; k < C.length; k++) {
Complex c = C[k];
Picture p = P[k];
String fileName = "C:\\Users\\86196\\Desktop\\图片\\a_" + k + ".jpg"; // 使用不同的标识符来区分文件名
for (int i = 0; i < p.getWidth(); i++) {
for (int j = 0; j < p.getHeight(); j++) {
// 对每一个像素点
int[] coordinate = {i, j};
// 得到z复数
Complex z = change(i, j);
// 判断这个点是不是有效的
int valid = isValid(z, c);
// 着色点
int grayscale = 255 - valid; // 使用迭代次数作为灰度级
Color color = new Color(grayscale, grayscale, grayscale);
p.setColor(i, j, color);
}
}
p.save(fileName); // 使用不同的文件名保存图片
}
最外层的对k的循环是对三张图片逐一操作。然后定义了String fileName 随着k而改变,方便对不同图片的保存。然后对每张图片的每个像素点循环,逐一得到z复数,判断这个点的灰度值,然后染色。当对这张图片的所有点遍历过后,保存图片。
五张图片出现在指定的位置:
五张图片如下:
实验3
创建一个购物车类类型(ShoppingCart),每一个 ShoppingCart 对象应该包含若干个商品(Item类型)对象,购物车类型应该至少提供添加商品、删除商品以及汇总购物车中商品价格的能力。这是一个开放题目,发挥的空间很大。下面是对该题完成所需满足的基本要求:
要求 1:商品类型至少要有三种以上(不同类型的商品有不同的属性),为了复用和多态,请使用继承完成商品这一组类型的构建;
要求 2:不论是商品类型,还是购物车类型都需要实现 toString 方法,针对不同类型中包含的不同数据成员订制输出内容;
要求 3:购物车对象应该不限制包含商品的数量,这时需要使用具有可动态扩容能力的数组数据类型(ArrayList),这个类型是 Java API 中已经定义好的数据类型。
要求 4:购物车的商品要能排序,因此要求商品类必须实现 Comparable 接口,排序算法可以使用冒泡排序算法。商品排序的规则自行规定,需要在实验报告中说明。
要求 5:该题在实验报告中的设计环节描述请使用 UML 类图完成。
要求 6:编写一个客户端程序,要有商品添加、商品删除、商品价格汇总以及商品排序等环节的调用和结果展示。
在Item类中,分别有四个private的int id、String name、double price、String describe,表示商品的ID,名称,价格和对商品的描述。
在Clothing类中,定义了private的String size和String color。
在Food类中,定义了private的Date startTime和Date endTime表示食品的保质期。
在Book类中,定义了private的String author表示书的作者。
在ShoppingCart类中,定义了private static double totalPrice表示一个购物车中所有商品的总价,同时使用具有可动态扩容能力的数组数据类型ArrayList<Item>储存商品。
类的关系与设计:
首先是一个抽象类Item,我不希望创建item的实例,只希望创建它的子类的实例。item包括的子类有三个,food,clothing和book。我们希望这三个类都可以实现Compare,于是我们让他们的父类item实现Comparable接口。最后是shoppingCart类,目的是实现购物车的各种功能。最后是一个测试类shoppingApp(没有画到UML类图中)
类中方法的设计:
2.所有的类中都按照题目要求的重写了toString方法,可以根据不同的类型生成不一样的内容。
3.根据题目的要求,在Item类中重写了compareTo方法,用来比较不同商品的单价,返回-1、0、1,再在shoppingCart类中的CompareItem方法调用compareTo,用来根据购物车商品的单价进行排序。
4.是Item和它的三个子类中,各种private属性变量的get和set方法。
5.shoppingCart类中添加了得到ArrayList<Item>和totalPrice的get方法,并且实现了根据Item向ArrayList中添加商品,以及两种根据商品名称和id从ArrayList中去除商品d方法。
shoppingCart类:
shoppingCart类中得到totalPrice的方法:
public double getTotalPrice() {
double temp = 0;
for (Item item : items) {
temp += item.getPrice();
}
totalPrice = temp;
return totalPrice;
}
遍历整个Item列表,取出所有item并得到他们的单价,将单价加到double temp中,返回temp就是totalPrice
shoppingCart类中重写的toString方法:
@Override
public String toString() {
totalPrice = getTotalPrice();
StringBuilder s = new StringBuilder("ShoppingCart's totalPrice: " + totalPrice + ", items包括: ");
for (Item item : items) {
s.append(item.getName());
s.append(", ");
}
//首先检查s的长度是否大于0,以防止在空的StringBuilder上调用substring方法。
if (s.length() > 0) {
int endIndex = s.length() - 2;
if (s.substring(endIndex).equals(", ")) {
s.delete(endIndex, s.length());
}
}
return s.toString();
}
个人感觉这段代码很精妙,首先调用本类里的方法getTotalPrice得到总价格,然后建立一个StringBulider s,后续可以添加内容,s.apppend每个item的名字和", ",但是此时发现最后还会多出来一个", ",想办法把它删掉。
首先检查s的长度是否大于0,以防止在空的StringBuilder上调用substring方法。如果不是空的,那么我们找到", "的索引,判断s的最后两个字符是不是", ",如果是的话,就根据索引删除最后两个字符。
最后返回转变成String类型的s。
shoppingCart类中的两个remove方法:
public void removeItemById(int id, int wantNumber) {
Iterator<Item> iterator = items.iterator();
int temp = 0;
while (iterator.hasNext() && temp < wantNumber) {
Item i = iterator.next();
if (i.getId() == id) {
iterator.remove();
temp++;
}
}
}
public void removeItemByName(String name, int wantNumber) {
Iterator<Item> iterator = items.iterator();
int temp = 0;
while (iterator.hasNext() && temp < wantNumber) {
Item i = iterator.next();
if (name.equals(i.getName())) {
iterator.remove();
temp++;
}
}
}
需要格外注意的就是需要使用迭代器来删除ArrayList中的元素,因为在遍历ArrayList的过程中,直接调用remove方法删除元素会导致ConcurrentModificationException。
shoppingCart类中的compareItem方法:
public void CompareItem() {
for (int i = 0; i < items.size() - 1; i++) {
for (int j = 0; j < items.size() - 1 - i; j++) {
if (items.get(j).compareTo(items.get(j + 1)) > 0) {
Item temp = items.get(j);
items.set(j, items.get(j + 1));
items.set(j + 1, temp);
}
}
}
}
很简单,直接根据商品的单价进行冒泡排序即可。
Item类和Comparable接口:
重写的compareTo方法:
@Override
//返回-1,0,1
public int compareTo(Item other) {
return Double.compare(this.price, other.price);
}
package homework0203;
public interface Comparable {
public int compareTo(Item i);
}
很简单的实现,不再赘述了。
三个子类比较类似,这里选取Food类为例:
重写的toString方法;
@Override
public String toString() {
return "Food{" + super.toString() + ", " + "startTime=" + startTime + ", endTime=" + endTime + '}';
}
通过直接return String中间混杂super的toString方法可以快速得到我想要输出的内容。
测试类ShoppingApp:
Try catch以及Food的创建:
//创建新的食物类型的对象比较麻烦,因为作死写了保质期
try{
//定义许多商品类
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date sdate1 = new Date();
Date edate1 = new Date();
sdate1 = format.parse("2023-10-01");
edate1 = format.parse("2024-10-01");
Food noodles = new Food(1,"Instant noodles",6,"nice",sdate1,edate1);
...........
}catch(Exception e){
System.out.println("Exception!!");
}
因为有Date类型我们用try catch把全文包起来。
我们的Date类型是希望得到食物的生产日期和过期日期,为了保证格式统一我们用SimpleDateFormat format,然后再创建Food的实例。
添加实例到列表中:
//增加到列表中
ShoppingCart sc = new ShoppingCart();
sc.addItem(noodles, 5);
sc.addItem(mybook, 3);
sc.addItem(noodles, 2);
sc.addItem(coat1,2);
sc.addItem(coat2,3);
sc.addItem(pants,5);
用两种不同的方式删除购物车中的商品:
//用两种不同的方式删除,可以选择删除
sc.removeItemById(1,1);
sc.removeItemByName("nice book",1);
sc.removeItemByName("coat2",10);
排序前查询一次:
//查询
System.out.println("开始查询");
System.out.println("----------------------------------------------------------------------------------");
List<Item> item = sc.getItems();
for(Item i : item)
System.out.println(i.toString());
System.out.println("结束查询");
System.out.println("----------------------------------------------------------------------------------");
根据单价排序并且再次查询:
//进行单价排序并且再次查询
sc.CompareItem();
System.out.println("开始查询");
System.out.println("----------------------------------------------------------------------------------");
for(Item i : item)
System.out.println(i.toString());
System.out.println("结束查询");
System.out.println("----------------------------------------------------------------------------------");
得到总价:
//商品价格汇总
double totalprice = sc.getTotalPrice();
System.out.println("购物车内商品总价格为"+totalprice);
这是我第二次使用多个类编写一个很具体的应用程序,区别在于,第一次我是看着UML类图来进行编写的,这一次是看着需求文档来进行编写的,感觉进步很大。复习了多个知识点,包括继承,接口,多态等基础语法和思想,也应用了ArrayList进行储存,并且复习了如何用迭代器删除ArrayList中的内容。学到了很多东西。最有收获的还是自己设计类之间的关系。
实验4
代码编写是对理论知识的一种实践。能够编写出正确、合理、有效的代码,理论知识必须得扎实。同学们需要沉下心来认真阅读参考资料、课件,再结合课堂所讲和课下所练,总结面向对象中对继承、接口以及多态的理解和认识。
- 封装
封装就是将对象的数据和基于数据的操作封装成一个独立性很强的模块,将对象不需要对外提供的私有数据和私有操作隐藏起来,外界要访问封装的数据需要依靠对象将某些操作公有化,在类中定义为对外的公共接口。
封装的好处:
良好的封装减少了耦合。(我们希望程序高内聚低耦合)
同时增强了数据的安全性,无法直接改变私有属性的值;
隐藏了内部实现细节,便于修改与代码重用。
重载(Overload)
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。最常用的地方就是构造器的重载。
重载规则:
被重载的方法必须改变参数列表(参数个数或类型不一样);
被重载的方法可以改变返回类型;
被重载的方法可以改变访问修饰符;
被重载的方法可以声明新的或更广的检查异常;
方法能够在同一个类中或者在一个子类中被重载。
无法以返回值类型作为重载函数的区分标准。
- 继承
继承是在当前类的基础上创建新类,并添加新的属性和方法。Java通过关键字extends实现子类继承父类。
继承的作用:继承可以降低代码编写的冗余度,实现代码的重用,提高程序的可维护性,节省大量创建新类的时间,提高开发效率和开发质量。
Java中继承的要点:
继承的基础是封装,在Java创建一个新类时,总是在继承,除非指明继承于一个指定类,否则都是隐式地从Java的根类Object中派生出来的子类,即Object类是所有类的“祖先”,Java 中的类一律继承了Object类的方法,这是Java的一大特色。
需要注意的是,Java 只支持类的单继承,每个子类只能有一个直接父类,不允许有两个以上的父类,这样使Java的继承方式很直接,代码简洁,结构清晰。
数据成员:
一个对象从其父类中继承数据成员和方法,但是不能直接访问从父类中继承的私有数据和方法,必须通过公有(或者保护)方法进行访问。
如果子类中定义有和父类中相同的成员变量名,那么从父类中继承而来的同名变量将会被隐藏。但可以用super去调用公有方法进行访问。
构造方法:
子类实例化时,默认调用父类的无参构造方法(有一个隐式的super())(不管子类的构造器有没有参数,因为子类继承的是父类的属性和方法,只调用父类的无参构造器就可以继承父类的属性和方法,因此不会调用父类的有参构造器),再调用子类的有参/无参构造器。
如果在子类构造函数中,调用了父类的有参构造方法,就不会再有隐式super()的调用父类的无参构造了。
注意:
super()必须写在子类构造函数的第一行。
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。
即构造方法不能被重写。
package homework0203;
class Father {
public Father(){
System.out.println("父类的无参构造方法");
}
public Father(int a ) {
System.err.println("父类的有参构造方法"+a);
}
}
class Son extends Father{
public Son() {
System.out.println("子类中的无参构造方法");
}
public Son(int a ) {
System.out.println("子类中的有参构造方法"+a);
}
}
public class TestExecute {
public static void main(String[] args) {
Son s = new Son();
System.out.println("---------------");
Son a = new Son(5);
}
}
但是父类中也可能没有无参构造:
package homework0203;
class Father1{
public Father1(int i) {
System.out.println("父类中的有参构造方法"+i);
}
}
class Son1 extends Father1{
public Son1(){
super(1);
System.out.println("子类中无参构造方法");
}
public Son1(int i) {
super(i);
System.out.println("子类中有参构造方法"+i);
}
}
public class TestExtend {
public static void main(String[] args) {
Son1 b = new Son1();
System.out.println("-----");
Son1 a = new Son1(5);
}
}
总结:
父类有无参构造函数时(显示或隐式),子类的有参和无参构造函数都是默认调用父类的无参构造函数;
当父类只有有参构造函数时,子类可以有有参和无参构造函数,子类有参构造函数必须显式调用父类的有参构造函数,子类无参构造函数也必须显式调用父类的有参构造函数,但需给父类有参构造函数赋确定的实参。
成员方法和重写(Override):
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要实现父类的方法。
方法的重写规则
1.子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
2.子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
(1)返回值是基本数据类型时,必须和父类的返回值类型相同
(2)返回值是引用数据类型时(类类型),子类重写方法返回的类型应该是父类被重写方法返回的类型或者其子类。(小于等于)
3.子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
注意:子类不能重写父类中声明为private权限的方法
4.子类方法抛出的异常不能大于父类被重写方法的异常,不能申明更宽泛的异常。
5.构造方法不能被重写。
6.声明为 final 的方法不能被重写。
7.声明为 static 的方法不能被重写,但是能够被再次声明。不是很理解重写和声明的根本区别,但是static不要写@Override就可以。
理解:
2.子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
举例:
package homework0203;
// 父类
class SuperClass{
public SuperClass() {
System.out.println("fulei");
}
public People create(){
System.out.println("父类");
return new People();
}
}
// 子类
class SubClass extends SuperClass{
public SubClass() {
System.out.println("zilei");
}
public Student create(){
System.out.println("子类");
return new Student();
}
}
class People{
}
class Student extends People{
}
public class OverrideTest {
public static void main(String[] args) {
SuperClass test = new SubClass();
//多态性,return new D(),故返回类型应该小于等于编译时类型(C类,父类中被重写的返回值类型)
People testC = test.create();
}
}
因为多态,我们将子类向上转型为父类后,调用父类重写的方法,得到返回值,因为java是个强类型语言,所以编译期间要给返回值赋予类型。因为,编译期间的返回值类型(父类的)需要大于等于运行时期的返回值类型(子类的)。
当调用create方法时,可能返回子类重写的方法返回的一个对象,也可能返回父类原本的方法返回的对象,在编译阶段是不知道具体是哪个的,所以要保证子类返回的类型(Student)小于父类返回的类型(People)。
另外,“成员变量,静态方法看左边;非静态方法:编译看左边,运行看右边。”,我们对static静态方法除外的方法--实例方法(包括构造方法)都是看右边去运行的。
3.子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
在使用多态的时候经常把子类向上转型为父类,进而父类.方法(形参列表)。而“父类.方法(形参列表)”这个语句能够编译成功没有报错,说明该方法的权限修饰符是可以在调用该方法的语句块中执行的,所以如果子类的被重写的方法的权限修饰符是大于等于父类该方法的权限修饰符,那么多态时就一定可以调用子类重写的方法,也就是运行时权限修饰符要大于等于编译器权限修饰符,这样才能保证运行时满足权限。
会在编译时报错:
4.子类方法抛出的异常不能大于父类被重写方法的异常
如果子类重写的方法抛出的异常更大,那么在多态的情况下, 父类的引用调用的方法只能看到小范围的异常,代码运行的时候,才知道具体是调用了那个子类对象的方法,如果此时子类对象的方法抛出的异常更大,从逻辑上来说,是错误的。
7.声明为 static 的方法不能被重写,但是能够被再次声明。
举例:
class Father1{
public Father1(int i) {
System.out.println("父类中的有参构造方法"+i);
}
public static void say(){
System.out.println("父类的say");
}
public void walk(){
System.out.println("父类的walk");
}
}
class Son1 extends Father1{
public Son1(){
super(1);
System.out.println("子类中无参构造方法");
}
public Son1(int i) {
super(i);
System.out.println("子类中有参构造方法"+i);
}
@Override
public static void say() {
System.out.println("子类的say");
}
@Override
public void walk(){
System.out.println("子类的walk");
}
}
- 多态
多态即在一个继承关系的程序中允许同名的不同方法共存,多态有三个必要的条件:继承、重写、父类指向子类对象。
在继承关系下,利用向上转型,子类的对象转化为父类的对象,与动态绑定相结合,通过父类对象调用具有相同名称的子类方法,JVM能够自动分辨出对象用的方法所属的子类,从而调用相应子类的方法。
理解向上转型的应用:
本质:父类的引用指向子类的对象
例如父类Animal,子类Cat,Dog。其中Animal可以是类也可以是接口,Cat和Dog是继承或实现Animal的子类。
Animal animal = new Cat();
即声明的是父类,实际指向的是子类的一个对象。我定义了一个Animal类型的引用,指向新建的Cat类型的对象。
这样可以降低程序的耦合性,即调用者不必关心调用的是哪个对象,只需要针对接口编程就可以了,被调用者对于调用者是完全透明的。让你更关注父类能做什么,而不去关心子类是具体怎么做的,你可以随时替换一个子类,也就是随时替换一个具体实现,而不用修改其他.
向上转型:
动态绑定:
调用对象方法的时候,该方法会和该对象的运行类型绑定:
静态方法看右边;实例方法:编译类型看左边,运行类型看右边(包括构造方法和实例方法、不包括静态方法,静态方法也是看左边(不建议通过实例调用静态方法,没有意义))
当调用对象属性时,没有动态绑定机制,即哪里声明,哪里使用:
可以调用父类的所有成员(需遵守访问权限),不能调用子类的特有成员
动态链接的例子:
package homework0203;
//演示动态绑定
public class DynamicBinding {
public static void main(String[] args) {
//向上转型(自动类型转换)
//程序在编译阶段只知道 p1 是 Person 类型
//程序在运行的时候才知道堆中实际的对象是 Student 类型
Person p1 = new Student();
//程序在编译时 p1 被编译器看作 Person 类型
//因此编译阶段只能调用 Person 类型中定义的方法
//在编译阶段,p1 引用绑定的是 Person 类型中定义的 mission 方法(静态绑定)
//程序在运行的时候,堆中的对象实际是一个 Student 类型,而 Student 类已经重写了 mission 方法
//因此程序在运行阶段对象中绑定的方法是 Student 类中的 mission 方法(动态绑定)
p1.mission();
//当调用对象属性时,没有动态绑定机制,即哪里声明,哪里使用
//在编译阶段,p1 引用绑定的是 Person 类型
//因此程序在运行阶段对象中绑定的是依旧是 Person 类型
System.out.println(p1.name);
}
}
//父类
class Person {
public String name = "person";
public void mission() {
System.out.println("人要好好活着!");
}
}
//子类
class Student extends Person {
public String name = "student";
@Override
public void mission() {
System.out.println("学生要好好学习!");
}
}
向下转型:
条件:
1.并不是所有的对象都可以向下转型,只有当这个对象原本就是子类对象通过向上转型得到的时候才能够成功转型。
2.向下转型只能回到原来的。
错误代码:
- Person p1 = new Student();
- Teacher t1 = (Teacher) p1;
上述代码虽然能够编译成功,但是在运行的时候会报错,因为fruit对象是由Apple对象向上转型得到的,只能够向下转型成Apple对象,不能够向下转型成Orange对象。
所以我们用instanceof为了避免上述类型转换异常的问题,我们引出 instanceof 比较操作符,用于判断对象的类型是否为XX类型或XX类型的子类型。
格式:对象 instanceof 类名称
解释:这将会得到一个boolean值结果,也就是判断前面的对象能不能当作后面类型的实例
public class test {
public static void main(String[] args) {
//向上转型
Person p1 = new Student();
//调用的是 Student 的 mission
p1.mission();
//向下转型
//利用 instanceof 进行判断
if(p1 instanceof Student) { //判断对象 p1 是否是 Student 类 的实例
Student s1 = (Student)p1;
s1.score(); //调用的是 Student 的 score
//上面这两句也可简写为 ((Student) p1).score();
}
else if(p1 instanceof Teacher){ //判断对象 p1 是否是 Teacher 类 的实例
Teacher t1 = (Teacher)p1;
t1.salary(); //调用的是 Teacher 的 salary
//同理,上面这两句也可简写为 ((Teacher) p1).salary();
}
}
}
一种使用多态的流程:
向上转型
调用子类父类公有的方法
Instanceof判断
向下转型
调用子类特有的方法
- 接口与抽象类
官方解释:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
接口的用途之一是实现多重继承,一个类通过接口使用其他多个类的资源。类也以接口方式将其他不相关的类整合到自己类中,并在此基础上进-一步扩展, 集合而成一个新的、可操作的新系统,实现了多重继承。
接口的用途之二在于它是一种规范,它规定了一组类在实现某些功能时必须拥有的统一规则,它屏蔽了相关功能的实施细节,以一种标准模式即接口方式提供给外部类或系统使用。实现了功能与系统的分离
①多态的实例化用implement关键字
②接口不能被直接实例化,可以通过多态的形式实现实例化
③接口中都是抽象方法和常量,不可能有非抽象方法,常量必须被赋值。
常量默认被public static final
方法默认被public static abstract修饰
④多态的前提:
有继承或实现关系
有方法的重写,
有父类或父接口引用指向子类或实现类对象
⑤接口的实现类
必须重写接口中的所有方法,不想重写则可以是抽象类(加上abstract)
但是子类继承了该抽象类必须重写接口中的方法。
在具体类实现的接口方法必须显式地定义为public,因为接口方法声明默认为public abstarct的,否则无法编译通过。
⑥接口只能被public修饰
⑦接口允许多继承:
接口如同类一样具有继承的功能,派生出子接口,与类不同的是,接口允许多重继承
当具体类实现一个子接口时,需要实现子接口连同其所有父接口中的所有抽象方法
接口和抽象类的异同:
抽象类和接口在定义方面有一定的相似性,都具有抽象方法,抽象方法的实现都放在其他类中实现。
二者的最大区别在于抽象类除了有抽象方法外还可以有一般方法的实现,而接口除了允许静态和默认方法外。只能是”纯抽象方法“。接口方法必须是public的,而抽象类中的方法可以是除private以外的其他类型。
- 异常处理
Java程序在执行过程中所发生的异常事件可分为两类:
Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError(栈溢出)和OOM(内存溢出)。一般不编写针对性的代码进行处理。
Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如:
空指针访问
试图读取不存在的文件
网络连接中断
数组角标越界
异常体系结构
Java标准库内建了一些通用的异常,这些类以Throwable为顶层父类。Throwable又派生出Error类和Exception类。
错误:Error类以及它的子类的实例,代表了JVM本身的错误。错误不能被程序员通过代码处理,Error很少出现。因此,程序员应该关注Exception为父类的分支下的各种异常类。
异常:Exception以及它的子类,代表程序运行时发生的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。
Error和Exception都有一个共同的根类是Throwable类。
Error是系统中的错误,程序员是不能改变的和处理的,一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。因此我们编写程序时不需要关心这类错误。
Exception,也就是我们经常见到的一些异常情况,表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
java的异常(包括Exception和Error)分为检查异常(checked exceptions)和非检查的异常(unchecked exceptions)。
其中根据Exception异常进行划分,可分为运行时异常和非运行时异常。
Try catch finally:
package homework0203;
import java.util.InputMismatchException;
import java.util.Scanner;
public class Test_Input_01 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
/*
* 当我们只写一个try{}的时候,会报错:
* Syntax error, insert "Finally" to complete TryStatement
* 语法错误,插入“Finally”来完成Try块
*
* 总结: try不能单独存在,必须结合catch或finally来使用
* try块中没有异常,会执行finally(如果有)
* try块中有异常,会执行对应的catch块(如果有),try中异常发生点之后的代码将不会执行
*/
try{
//try块中放可能发生异常的代码。
//如果执行完try且不发生异常,则接着去执行finally块和finally后面的代码(如果有的话)。
//如果发生异常,则尝试去匹配catch块。异常点之后的代码不会运行
//这行代码有可能出错
int num1 = sc.nextInt(); //这行代码如果出现异常,那么后面的输出语句就不执行
int num2 = sc.nextInt();
System.out.println(num1+"\t"+num2);
System.out.println(num1/num2);
String str = null;
System.out.println(str.charAt(0));
}catch(InputMismatchException | NullPointerException e) {
// catch(异常对象) 当异常发生了,catch就会捕获对应的异常
// 每一个catch块用于捕获并处理一个特定的异常,或者这异常类型的子类。Java7中可以将多个异常声明在一个catch中。 catch(Exception1 | Exception2 | Exception3 e)
// catch后面的括号定义了异常类型和异常参数。如果异常与之匹配且是最先匹配到的,则虚拟机将使用这个catch块来处理异常。
// 在catch块中可以使用这个块的异常参数来获取异常的相关信息。异常参数是这个catch块中的局部变量,其它块不能访问。
// 如果当前try块中发生的异常在后续的所有catch中都没捕获到,则先去执行finally,然后到这个函数的外部caller中去匹配异常处理器。
// 如果try中没有发生异常,则所有的catch块将被忽略。
System.out.println("catch块 - try里面发生了异常 - 空指针和输入不匹配异常都走这个Catch");
}catch(java.lang.ArithmeticException e){
e.getMessage();
System.out.println("算数异常:除数不能为0");
}catch(Exception e){
System.out.println("程序发生未知异常");
}finally {
// finally块通常是可选的。
// 无论异常是否发生,异常是否匹配被处理,finally都会执行。
// 一个try至少要有一个catch块,否则, 至少要有1个finally块。但是finally不是用来处理异常的,finally不会捕获异常。
// finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。
System.out.println("finally块");
}
System.out.println("异常捕获之后的代码");
}
}
需要注意的地方
1、try块中的局部变量和catch块中的局部变量(包括异常变量),以及finally中的局部变量,他们之间不可共享使用。
2、每一个catch块用于处理一个异常。异常匹配是按照catch块的顺序从上往下寻找的,只有第一个匹配的catch会得到执行。匹配时,不仅运行精确匹配,也支持父类匹配,因此,如果同一个try块下的多个catch异常类型有父子关系,应该将子类异常放在前面,父类异常放在后面,这样保证每个catch块都有存在的意义。
3、java中,异常处理的任务就是将执行控制流从异常发生的地方转移到能够处理这种异常的地方去。也就是说:当一个函数的某条语句发生异常时,这条语句的后面的语句不会再执行,它失去了焦点。执行流跳转到最近的匹配的异常处理catch代码块去执行,异常被处理完后,执行流会接着在“处理了这个异常的catch代码块”后面接着执行。
有的编程语言当异常被处理后,控制流会恢复到异常抛出点接着执行,这种策略叫做:resumption model of exception handling(恢复式异常处理模式)
而Java则是让执行流恢复到处理了异常的catch块后接着执行,这种策略叫做:termination model of exception handling(终结式异常处理模式)
Throw和throws:
package homework0203;
public class StudentTest {
public static void main(String[] args) {
try {
Student s = new Student();
s.regist(-1001);
System.out.println(s);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
class Student {
private int id;
public void regist(int id) throws Exception {
if (id > 0) {
this.id = id;
} else {
//手动抛出异常对象
throw new Exception("您输入的数据非法!");
}
}
}
throw语句用在方法体内,表示抛出异常,由方法体内的语句处理。
throws出现在方法函数头,表示在抛出异常,由该方法的调用者来处理。
throws主要是声明这个方法会抛出这种类型的异常,使它的调用者知道要捕获这个异常。throw是具体向外抛异常的动作,所以它是抛出一个异常实例。
附录:
package homework0201;
public class Complex {
private double real = 0.0;
private double imag = 0.0;
private final double EPSILON = 1e-10;
public Complex() {
}
public Complex(double real, double imag) {
this.real = real;
this.imag = imag;
}
public double getReal() {
return real;
}
public void setReal(double real) {
this.real = real;
}
public double getImag() {
return imag;
}
public void setImag(double imag) {
this.imag = imag;
}
public void setValue(double real, double imag) {
this.real = real;
this.imag = imag;
}
@Override
public String toString() {
if (imag >= 0) return real + " +" + imag + "i";
else return real + "" + imag + "i";
}
public boolean isReal() {
return imag < EPSILON;
}
public boolean isImaginary() {
return real < EPSILON;
}
public boolean equals(double real, double imag) {
return ((real - this.real) <= EPSILON) && ((this.real - real) <= EPSILON) &&
((imag - this.imag) <= EPSILON) && ((this.imag - imag) <= EPSILON);
}
public boolean equals(Complex c) {
double real = c.getReal();
double imag = c.getImag();
return ((real - this.real) <= EPSILON) && ((this.real - real) <= EPSILON) &&
((imag - this.imag) <= EPSILON) && ((this.imag - imag) <= EPSILON);
}
public double abs() {
return Math.sqrt((real * real) + (imag * imag));
}
public Complex add(Complex c2) {
Complex c3 = new Complex();
c3.setImag(c2.getImag() + imag);
c3.setReal(c2.getReal() + real);
return c3;
}
public Complex subtract(Complex c2) {
Complex c3 = new Complex();
c3.setImag(imag - c2.getImag());
c3.setReal(real - c2.getReal());
return c3;
}
public Complex multiply(Complex c2) {
Complex c3 = new Complex();
c3.setReal(real * c2.getReal() - imag * c2.getImag());
c3.setImag(real * c2.getImag() + imag * c2.getReal());
return c3;
}
public Complex divide(Complex c2) throws ArithmeticException{
Complex c3 = new Complex();
double temp = c2.getImag() * c2.getImag() + c2.getReal() * c2.getReal();
// Check if the divisor is zero
if (temp == 0) {
throw new ArithmeticException("Division by zero");
}
c3.setReal((real * c2.getReal() + imag * c2.getImag()) / temp);
c3.setImag((imag * c2.getReal() - real * c2.getImag()) / temp);
return c3;
}
}
TestComplex类:
package homework0201;
public class TestComplex {
public static void main(String[] args) {
// Test the Constructor method
Complex c1 = new Complex();
Complex c4 = new Complex(2.0, 3.0);
// Test the setReal() and getReal() method
c1.setReal(3.0);
double real = c1.getReal();
System.out.println("Real part: " + real);
// Test the getImag() and setImag() method
c1.setImag(4.0);
double imag = c1.getImag();
System.out.println("Imaginary part: " + imag);
// Test the setValue() method
c1.setValue(4.0, 5.0);
System.out.println("New complex number: " + c1);
// Test the isReal() method
boolean isReal = c1.isReal();
System.out.println("Is real? " + isReal);
// Test the isImaginary() method
boolean isImaginary = c1.isImaginary();
System.out.println("Is imaginary? " + isImaginary);
// Test the equals(double real, double imag) method
boolean equals1 = c1.equals(4.0, 5.0);
System.out.println("Equals (4.0, 5.0)? " + equals1);
// Test the equals(Complex c) method
Complex c2 = new Complex(4.0, 5.0);
boolean equals2 = c1.equals(c2);
System.out.println("Equals (4.0, 5.0)? " + equals2);
// Test the abs() method
double abs = c1.abs();
System.out.println("Absolute value: " + abs);
// Test the add(Complex c2) method
Complex c3 = new Complex(1.0, 2.0);
Complex sum = c1.add(c3);
System.out.println("Sum: " + sum);
// Test the subtract(Complex c2) method
Complex difference = c1.subtract(c3);
System.out.println("Difference: " + difference);
// Test the multiply(Complex c2) method
Complex product = c1.multiply(c3);
System.out.println("Product: " + product);
// Test the divide(Complex c2) method
try{
Complex quotient = c1.divide(c3);
System.out.println("Quotient: " + quotient);
}catch (ArithmeticException e){
System.out.println("The divisor is 0");
}
}
}
ComplexApp类:
package homework0201;
import java.util.Scanner;
public class ComplexApp {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter complex number 1 (real and imaginary part): ");
double real1 = scanner.nextDouble();
double imag1 = scanner.nextDouble();
Complex c1 = new Complex(real1, imag1);
System.out.print("Enter complex number 2 (real and imaginary part): ");
double real2 = scanner.nextDouble();
double imag2 = scanner.nextDouble();
Complex c2 = new Complex(real2, imag2);
System.out.println("Number 1 is: " + c1);
if (c1.isReal()) {
System.out.println("(" + c1 + ")" + " is a pure real number");
} else {
System.out.println("(" + c1 + ")" + " is NOT a pure real number");
}
if (c1.isImaginary()) {
System.out.println("(" + c1 + ")" + " is a pure imaginary number");
} else {
System.out.println("(" + c1 + ")" + " is NOT a pure imaginary number");
}
System.out.println("Number 2 is: " + c2);
if (c2.isReal()) {
System.out.println("(" + c2 + ")" + " is a pure real number");
} else {
System.out.println("(" + c2 + ")" + " is NOT a pure real number");
}
if (c2.isImaginary()) {
System.out.println("(" + c2 + ")" + " is a pure imaginary number");
} else {
System.out.println("(" + c2 + ")" + " is NOT a pure imaginary number");
}
if (c1.equals(c2)) {
System.out.println("(" + c1 + ")" + " is equal to " + "(" + c2 + ")");
} else {
System.out.println("(" + c1 + ")" + " is NOT equal to " + "(" + c2 + ")");
}
Complex sum = c1.add(c2);
System.out.println("(" + c1 + ")" + " + " + "(" + c2 + ")" + " = " + "(" + sum + ")");
}
}
package homework0202;
import java.awt.*;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
//定义
Picture[] P = new Picture[5];
P[0] = new Picture(1024,1024);
P[1] = new Picture(1024,1024);
P[2] = new Picture(1024,1024);
P[3] = new Picture(1024,1024);
P[4] = new Picture(1024,1024);
Complex[] C = new Complex[5];
C[0] = new Complex(-0.285, 0.156);
C[1] = new Complex(0.285, -0.01);
C[2] = new Complex(0.38, -0.18);
C[3] = new Complex(-0.835, -0.2321);
C[4] = new Complex(-0.7269, 0.1889);
for (int k = 0; k < C.length; k++) {
Complex c = C[k];
Picture p = P[k];
String fileName = "C:\\Users\\86196\\Desktop\\图片\\a_" + k + ".jpg"; // 使用不同的标识符来区分文件名
for (int i = 0; i < p.getWidth(); i++) {
for (int j = 0; j < p.getHeight(); j++) {
// 对每一个像素点
int[] coordinate = {i, j};
// 得到z复数
Complex z = change(i, j);
// 判断这个点是不是有效的
int valid = isValid(z, c);
// 着色点
int grayscale = 255 - valid; // 使用迭代次数作为灰度级
Color color = new Color(grayscale, grayscale, grayscale);
p.setColor(i, j, color);
}
}
p.save(fileName); // 使用不同的文件名保存图片
}
}
//把图片处理的左上角为原点的坐标轴转变为复数运算的中心为原点的坐标轴xy
public static Complex change(int i, int j) {
//匹配最近的点
double x = ((double) i / 256) - 2;
double y = 2 - ((double) j / 256);
return new Complex(x, y);
}
public static int isValid(Complex z0, Complex c) {
Complex z = z0;
int max = 255;
for (int t = 0; t < max; t++) {
if (z.abs() > 2) return t;
z = z.multiply(z).add(c);
}
return max;
}
}
package homework0202;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class Picture {
private BufferedImage image;
public int width;
private int height;
public Picture(String filename) throws IOException {
File file = new File(filename);
image = ImageIO.read(file);
width = image.getWidth();
height = image.getHeight();
}
public Picture(int width, int height){
this.width = width;
this.height = height;
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
public void setColor(int col, int row, Color c){
image.setRGB(col, row, c.getRGB());
}
public Color getColor(int col, int row){
int rgb = image.getRGB(col, row);
return new Color(rgb);
}
public void save(String filename) throws IOException {
String suffix = filename.substring(filename.lastIndexOf('.')+1);
if (suffix.equalsIgnoreCase("png") || suffix.equalsIgnoreCase("jpg"))
ImageIO.write(image, suffix, new File(filename));
}
public void darker(){
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++){
Color c = getColor(i, j);
setColor(i, j, c.darker());
}
}
}
Complex类:
package homework0202;
public class Complex {
private double real = 0.0;
private double imag = 0.0;
private final double EPSILON = 1e-10;
public Complex() {
}
public Complex(double real, double imag) {
this.real = real;
this.imag = imag;
}
public double getReal() {
return real;
}
public void setReal(double real) {
this.real = real;
}
public double getImag() {
return imag;
}
public void setImag(double imag) {
this.imag = imag;
}
public void setValue(double real, double imag) {
this.real = real;
this.imag = imag;
}
@Override
public String toString() {
if (imag >= 0) return real + " +" + imag + "i";
else return real + "" + imag + "i";
}
public boolean isReal() {
return imag < EPSILON;
}
public boolean isImaginary() {
return real < EPSILON;
}
public boolean equals(double real, double imag) {
return ((real - this.real) <= EPSILON) && ((this.real - real) <= EPSILON) &&
((imag - this.imag) <= EPSILON) && ((this.imag - imag) <= EPSILON);
}
public boolean equals(Complex c) {
double real = c.getReal();
double imag = c.getImag();
return ((real - this.real) <= EPSILON) && ((this.real - real) <= EPSILON) &&
((imag - this.imag) <= EPSILON) && ((this.imag - imag) <= EPSILON);
}
public double abs() {
return Math.sqrt((real * real) + (imag * imag));
}
public Complex add(Complex c2) {
Complex c3 = new Complex();
c3.setImag(c2.getImag() + imag);
c3.setReal(c2.getReal() + real);
return c3;
}
public Complex subtract(Complex c2) {
Complex c3 = new Complex();
c3.setImag(imag - c2.getImag());
c3.setReal(real - c2.getReal());
return c3;
}
public Complex multiply(Complex c2) {
Complex c3 = new Complex();
c3.setReal(real * c2.getReal() - imag * c2.getImag());
c3.setImag(real * c2.getImag() + imag * c2.getReal());
return c3;
}
public Complex divide(Complex c2) throws ArithmeticException{
Complex c3 = new Complex();
double temp = c2.getImag() * c2.getImag() + c2.getReal() * c2.getReal();
// Check if the divisor is zero
if (temp == 0) {
throw new ArithmeticException("Division by zero");
}
c3.setReal((real * c2.getReal() + imag * c2.getImag()) / temp);
c3.setImag((imag * c2.getReal() - real * c2.getImag()) / temp);
return c3;
}
}
package homework0203;
public abstract class Item implements Comparable {
private int id;
private String name;
private double price;
private String describe;
public Item() {
}
public Item(int id, String name, double price, String describe) {
this.id = id;
this.name = name;
this.price = price;
this.describe = describe;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
@Override
public String toString() {
return "id=" + id + ", name='" + name + '\'' + ", price=" + price + ", describe='" + describe + '\'' ;
}
@Override
//返回-1,0,1
public int compareTo(Item other) {
return Double.compare(this.price, other.price);
}
}
package homework0203;
import java.util.Date;
public class Food extends Item {
private Date startTime;
private Date endTime;
public Food() {
}
public Food(Date startTime, Date endTime) {
this.startTime = startTime;
this.endTime = endTime;
}
public Food(int id, String name, double price, String describe, Date startTime, Date endTime) {
super(id, name, price, describe);
this.startTime = startTime;
this.endTime = endTime;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
@Override
public String toString() {
return "Food{" + super.toString() + ", " + "startTime=" + startTime + ", endTime=" + endTime + '}';
}
}
package homework0203;
public class Clothing extends Item {
private String size;
private String color;
public Clothing() {
}
public Clothing(String size, String color) {
this.size = size;
this.color = color;
}
public Clothing(int id, String name, double price, String describe, String size, String color) {
super(id, name, price, describe);
this.size = size;
this.color = color;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Clothing{" + super.toString() + ", " + "size='" + size + '\'' + ", color='" + color + '\'' + '}';
}
}
package homework0203;
public class Book extends Item{
private String author;
public Book() {
}
public Book(String author) {
this.author = author;
}
public Book(int id, String name, double price, String describe, String author) {
super(id, name, price, describe);
this.author = author;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Book{" +super.toString()+ ", " + "author='" + author + '\'' + '}';
}
}
package homework0203;
import java.util.ArrayList;
import java.util.Iterator;
public class ShoppingCart {
private ArrayList<Item> items = new ArrayList<>();
static private double totalPrice;
public ShoppingCart() {
}
public ArrayList getItems() {
return items;
}
public double getTotalPrice() {
double temp = 0;
for (Item item : items) {
temp += item.getPrice();
}
totalPrice = temp;
return totalPrice;
}
@Override
public String toString() {
totalPrice = getTotalPrice();
StringBuilder s = new StringBuilder("ShoppingCart's totalPrice: " + totalPrice + ", items包括: ");
for (Item item : items) {
s.append(item.getName());
s.append(", ");
}
//首先检查s的长度是否大于0,以防止在空的StringBuilder上调用substring方法。
if (s.length() > 0) {
int endIndex = s.length() - 2;
if (s.substring(endIndex).equals(", ")) {
s.delete(endIndex, s.length());
}
}
return s.toString();
}
public void addItem(Item item, int wantNumber) {
for (int i = 0; i < wantNumber; i++) {
items.add(item);
}
}
public void removeItemById(int id, int wantNumber) {
Iterator<Item> iterator = items.iterator();
int temp = 0;
while (iterator.hasNext() && temp < wantNumber) {
Item i = iterator.next();
if (i.getId() == id) {
iterator.remove();
temp++;
}
}
}
public void removeItemByName(String name, int wantNumber) {
Iterator<Item> iterator = items.iterator();
int temp = 0;
while (iterator.hasNext() && temp < wantNumber) {
Item i = iterator.next();
if (name.equals(i.getName())) {
iterator.remove();
temp++;
}
}
}
public void CompareItem() {
for (int i = 0; i < items.size() - 1; i++) {
for (int j = 0; j < items.size() - 1 - i; j++) {
if (items.get(j).compareTo(items.get(j + 1)) > 0) {
Item temp = items.get(j);
items.set(j, items.get(j + 1));
items.set(j + 1, temp);
}
}
}
}
}
package homework0203;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
public class ShoppingApp {
public static void main(String[] args) {
//创建新的食物类型的对象比较麻烦,因为作死写了保质期
try{
//定义许多商品类
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date sdate1 = new Date();
Date edate1 = new Date();
sdate1 = format.parse("2023-10-01");
edate1 = format.parse("2024-10-01");
Food noodles = new Food(1,"Instant noodles",6,"nice",sdate1,edate1);
Book mybook = new Book(2,"book about computer",19.8,"good","hui");
Clothing coat1 = new Clothing(3,"coat1",98,"warmth","XXL","Black");
Clothing coat2 = new Clothing(4,"coat2",198,"beautiful","L","Pink");
Clothing pants = new Clothing(5,"pants",68,"warmth","XXL","Black");
//增加到列表中
ShoppingCart sc = new ShoppingCart();
sc.addItem(noodles, 5);
sc.addItem(mybook, 3);
sc.addItem(noodles, 2);
sc.addItem(coat1,2);
sc.addItem(coat2,3);
sc.addItem(pants,5);
//用两种不同的方式删除,可以选择删除
sc.removeItemById(1,1);
sc.removeItemByName("nice book",1);
sc.removeItemByName("coat2",10);
//查询
System.out.println("开始查询");
System.out.println("----------------------------------------------------------------------------------");
List<Item> item = sc.getItems();
for(Item i : item)
System.out.println(i.toString());
System.out.println("结束查询");
System.out.println("----------------------------------------------------------------------------------");
//进行单价排序并且再次查询
sc.CompareItem();
System.out.println("开始查询");
System.out.println("----------------------------------------------------------------------------------");
for(Item i : item)
System.out.println(i.toString());
System.out.println("结束查询");
System.out.println("----------------------------------------------------------------------------------");
//商品价格汇总
double totalprice = sc.getTotalPrice();
System.out.println("购物车内商品总价格为"+totalprice);
}catch(Exception e){
System.out.println("Exception!!");
}
}
}
package homework0203;
public interface Comparable {
public int compareTo(Item i);
}