201521123091 《Java程序设计》第4周学习总结

Java 第二周总结

第四周的作业。

目录
1.本章学习总结
2.Java Q&A
3.使用码云管理Java代码
4.PTA实验


1.本章学习总结

1.1 尝试使用思维导图总结有关继承的知识点。


1.2 使用常规方法总结其他上课内容。

多态在这边介绍一下:

  • 多态就是相同的形态,不同的行为。这样去理解,在面向对象的编程语言中,多态就是不同类型的对象通过相同的接口去实现不同的方法。那么我们可以通过类继承机制或者是接口来实现动态绑定,进而实现多态。
  • instanceof 测试一个对象是否是某个类的实例,即使左边是右边类的子类的实例对象,也会返回true。
  • 我们可以通过使用类型指派来完成子类所特有的某些方法。但是不能随便进行类型指派,否则会出现ClassCastException异常。要谨慎使用类型指派,所以经常会通过instanceof来检查。

2.Java Q&A

1.注释的应用:使用类的注释与方法的注释为前面编写的类与方法进行注释,并在Eclipse中查看。(截图)

  首先上我们的注释过后的类的文档:

package ex3;

/**
 * 照着String的文档,写一点点,英语不好,权且用中文吧。这个类呢,大概是用来描述公司里面的一个雇员。
 * 下面就不一一介绍这些成员变量了,因为意思很清晰,我没有随便命名变量。
 * 为了稳妥起见,我指定程序代码最早使用的版本为JDK5.0。主要没有好好研究下,是不是里面用了一些新版本的新特性。
 * <p>下面只是简单地试一些标签
 * <p>“@see”这个标签可以允许我们引用其他类的文档,生成HTML文件会生成超链接
 * <p>“@author”这个标签就是指明作者,LJL36就是我了
 * <p>“@return”用来描述返回值的含义
 * <p>“@param”用来描述参数列表中的标识符
 * @see java.lang.Object#toString()
 * @see java.lang.String
 * @author LJL36
 * @since JDK5.0
 */

class Person {
	private String name;
	private boolean gender;
	private int age;
	private int id;
	
	/**
	 * 这是一个无参构造器,会输出一个语句,然后将未初始化的默认成员变量值打印出来
	 */
	public Person() {
		// TODO Auto-generated constructor stub
		System.out.println("This is constructor");
		System.out.println(name + "," + age + "," + gender + "," + id);
	}
	
	/**
	 * 这是一个最常见的有参的构造器,就是把传进来的参数来初始化对象,
	 * 不过这边没有初始化id这个变量,所以会默认是0
	 * @param name
	 *        名字
	 *        
	 * @param age
	 * 	      年龄
	 * 
	 * @param gender
	 *        性别
	 */
	public Person(String name, int age, boolean gender) {
		this.name = name;
		this.gender = gender;
		this.age = age;
	}
	/**
	 * 这边重写了一个toString方法,是用eclipse自动生成的
	 * @return Person类的字符串表示
	 */
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "Person [name=" + name + ", age=" + age + ", gender=" + gender + ", id=" + id + "]";
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public boolean isGender() {
		return gender;
	}
	public void setGender(boolean gender) {
		this.gender = gender;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
}

  类的注释是用中文写的,因为英语不大好,用中文会流畅很多。类文档一上来应该简要的概括一下这个类。我测试的这个类非常的简单orz。所以也没什么太多的话说。所以我尝试加入一些参数的使用,具体的意思上面也都说了。最常用的几个就像@see,@author,@since这些了。
  

  有参构造器的注释:

  无参构造器的注释:

  toString方法的注释:

(当然一般在输出一个类的时候,如果已经写好了toString方法的话,就不需要显示加上,这边只是为了看看toString方法的注释而已)


2.面向对象设计(大作业1,非常重要)

2.1 将在网上商城购物或者在班级博客进行学习这一过程,描述成一个故事。(不得少于50字)

实变函数学十遍、学习泛函心犯寒

  最近要学习一下复变分析,据说Elias M. Stein写的Complex Analysis写的不错。于是先输入账户,密码和验证码登录了某购物网站:
  
  显示登陆成功!
  
  然后在搜索框中输入我要买的这本书“Complex Analysis”:

  下面出现了很多搜索结果,第一本就是我要的

  进入看详细信息,除了外面就可以看到的书名,价格,里面还可以看到:

  点击加入购物车
  
  我可以不止买一本,可以两本、三本,十几本……
  那去结算咯!再看一下价格,900+??,把我卖了都买不起啊,妥妥删了,随便去down个PDF下来吧。
  
  
  (上面都是我瞎扯的,没有时间学)

2.2 通过这个故事我们能发现谁在用这个系统,系统中包含的类及其属性方法,类与类之间的关系。尝试找到这些类与属性,并使用思维导图描述类、属性、方法及类与类之间的关系。

2.3 尝试使用Java代码实现故事中描述的这一过程(不必很完善),将来要在这个基础上逐渐完善、扩展成一个完整的面向对象的系统。(可选:加分)

  两题合在一起做了
  1.首先需要有一个启动类:启动购物系统,主要就是main方法在这个里面,主要就是要调用其他的类。

package shopping;

public class StartShoppingSystem {
	public static void main(String[] args) {
		
	}
}

  2.然后需要一个商品类Goods:最主要的属性就是商品名和价格

package shopping;

public class Goods {
	private String name;
	private double price;
}

PS:然后下面会继承其他的许多类,我这边写了些,比如图书,服装。主要是一个网上商城肯定有分类,而且这样易于搜索。然后还会有一个AllGoods类,按理来说数据操作都是在后台数据库完成的?这边就暂且加一个吧。
  3.搜索类:这样想的,凡是根据满足关键字条件的商品都加入列表中,并且显示出来,所以暂时先只要:

package shopping;

import java.util.ArrayList;

public class Search {
	private String keyWord;
	
	private ArrayList<Goods> arrayList = new ArrayList<Goods>();
	
	void search() {
		
	}
}

  4.菜单类,主要是和用户的交互,比如查看订单,查看个人消息,查看购物车,搜索之类的杂七杂八的:

package shopping;

public class Menu {
	public void startSearch() {
		Search search = new Search();
	}
	public void showMyself(User user) {
		
	}
	public void showShoppingCart(ShoppingCart shoppingCart) {
		
	}
}

  5.用户类,里面放了一些用户需要的属性,方法没怎么加。同样加了个AllUsers存放所有用户的信息。

package shopping;

public class User {
	String userName;
	String password;
	String address;
	
}

  6.购物车类,非常核心的一个类,不过同样地我也没怎么写东西,这边用到了组合,因为商品可以有数量。

package shopping;

import java.util.ArrayList;
import java.util.concurrent.BlockingDeque;

public class ShoppingCart {
	class Item {
		private Goods goods;
		private int num;
	}
	private ArrayList<Item> items = new ArrayList<Item>();
	private double totalPrice;
	
	public void add() {
		
	}
	
	public void delete() {
		
	}
	
	public void purchase() {
		
	}
	
	public void clear() {
		
	}
}

  7.登录类:主要就是把登录这个功能单独细分出来,然后因为有涉及到验证码,所以这边有加了个验证码的类,不知道真正的实现,随便写了个。

//Code.java
package shopping;

import java.io.File;

public class Code {
	File picture;
	String codeString;
}

//Login.java
package shopping;

public class Login {
	
	void showCode() {
		
	}
	
	boolean verify(String userName, String passWord, String code) {
		
		
		return true;
	}
	
	Code code;
}

  差不多就这样了,然后贴一下UML类图,大致现在的关系就是这样,因为很多属性没有考虑到,而且方法还没有具体实现,所以比较EZ:
  


3.分析ManagerTest.zip中的代码,回答几个问题:

3.1 在本例中哪里体现了使用继承实现代码复用?回答时要具体到哪个方法、哪个属性。

  首先,这边贴一下维基对于代码复用的定义:

Code reuse, also called software reuse, is the use of existing software, or software knowledge, to build new software,following the reusability principles.

  简言之,就是对曾经使用过的代码一部分甚至全部重新加以利用。那我们就记住这句话,然后来看看在ManagerTest.java中到底哪些地方体现了代码的复用。

  • 构造器
//Employee类
public Employee(String n, double s, int year, int month, int day) {
    name = n;
    salary = s;
    GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
    hireDay = calendar.getTime();
}

//Manager类
public Manager(String n, double s, int year, int month, int day) {
    super(n, s, year, month, day);
    bonus = 0;
}

  这边出现了super关键字,这是面向过程语言中一个重要的关键字,在构造器当中,我们上回提到了this这个关键字表示的是当前对象的引用。而相似地,我们也经常会通过super这个关键字去调用父类的构造器或者是父类的方法。这边我们就是在调用父类的构造器来完成子类对象的初始化。
  
  关于构造器这边还有要说的点

Note: If a constructor does not explicitly invoke a superclass constructor, the Java compiler automatically inserts a call to the no-argument constructor of the superclass. If the super class does not have a no-argument constructor, you will get a compile-time error. Object does have such a constructor, so if Object is the only superclass, there is no problem.

  如果子类的构造器没有显式调用父类的构造器,那么Java的编译器就会自己加一个父类的无参构造器,如果父类没有无参构造器,那么就会编译出错,这是什么情况呢,就是在父类写了个有参的构造器,然后子类的构造器没有显式调用这个有参的构造器,那么编译就会出错。就加入我们现在把这第一行删掉,就会发出“Implicit super constructor Employee() is undefined. Must explicitly invoke another constructor”的编译错误。

  • 成员变量
      这边就是name(String)、salary(double)和hireDay(Date)
      代码当中并没有很明显体现出哪里是复用了父类的成员,其实在上面我们在介绍构造器的时候,大家就可以发现,我们在Manager类中并没有定义任何成员变量,但是都没有出现编译出错的信息,究其原因就是子类复用了定义父类成员变量的代码,所以就不用再重复了。
      
  • 成员方法
//Employee类
public double getSalary() {
    return salary;
}
//Manager类
public double getSalary() {
    double baseSalary = super.getSalary();
    return baseSalary + bonus;
}

  这边是调用父类的方法。从这段代码我们更可以看出添加super关键字的重要性,如果将super去掉,那么就会产生我们并不希望看到的递归。主要这个函数还没有基准条件(Base Case)那么就会一直向下递归,直到簿记空间全部被塞满为止,然后电脑就跑崩了。
  说完继承之后,Java表示权限的四大关键字就已经全部出现了,下面分列它们各自的访问权限范围。
  

(PS:发现了博客园的第?个不支持:表格,这边把我用md语法实现的表格截下来)

  

3.2 Employee类及其子类Manager都有getSalary方法,那怎么区分这两个方法呢?

  区分两个同名方法?
  那大概想让编译器能够区分吧。
  首先,我们在上回提到,如何在一个类当中去区分同名方法,我们的解决办法是使用不同的参数列表,这个不同的范围非常大,即使我们使用了相同的参数,只要顺序不同,编译器都可以区分出来。
  那么,怎么去区分父类和子类的两个方法。其实上面我们也说过了,就是我们使用super关键字来表示父类,那么假如这个例子super.getSalary()就表示父类的getSalary()方法。因为我们会有递归啊,所以在方法内部出现了同名的方法,完全可以认为是递归,所以必须得用super来区分。
  
  

3.3 文件第26行e.getSalary(),到底是调用Manager类的getSalary方法还是Employee类的getSalary方法。

  贴图解决

  e都已经说明是Employee类的了,肯定调用Employee类的方法啊,而且调用子类的方法,听上去不是有点怪怪的吗,虽然查了下似乎是可以的,以一种曲线救国的方法,不过应该没什么必要,这边贴个链接,学到后面了回头再看。传说中的父类调用子类方法

  不好意思,上面是错误的示范。留在这边,当个反例。赶得太急了,也没有检查。这里面也是多态的体现。就是我们下面会说到的动态绑定,编译器无法得知到底是什么类的对象在调用这个方法,只有在运行的时候,才能够根据e的类型来确定。这边只是通过父类给所有的继承它的子类都提供同名方法的统一接口而已。(感谢老师指出错误)
  

3.4 Manager类的构造函数使用super调用父类的构造函数实现了代码复用,你觉得这样的有什么好处?为什么不把父类构造函数中的相关代码复制粘贴到Manager的构造函数中,这样看起来不是更直观吗?

  好处就是少写了一些代码。
  是很直观,但是犯了Don't repeat yourself.的错误。
  使用父类的构造器super()来帮助子类进行初始化,已经是一种约定俗成的方法,所以在代码的可读性方面并不输直接复制粘贴代码,而且还缩减了代码量,所以肯定是直接用super调用父类的构造器更好。(而且这样显得比较高大上,复制粘贴代码看上去很蠢。)
  这边又要提到上文我们说的代码复用的问题
  再引用一下wiki

Code reuse aims to save time and resources and reduce redundancy by taking advantage of assets that have already been created in some form within the software product development process.
......
These are some of the main goals behind the invention of object-oriented programming,which became one of the most common forms of formalized reuse.Code reuse

  代码复用就是为了要节省时间和资源,还要减少冗余。造成冗余的易读性是没有必要的。而且wiki还说了OOP就是为了更规范地复用代码而生的,然后如果还傻乎乎的复制粘贴,无话可说。


4.Object类

4.1 编写一个Fruit类及属性String name,如没有extends自任何类。使用System.out.println(new Fruit());是调用Fruit的什么方法呢?该方法的代码是从哪来的?尝试分析这些代码实现了什么功能?

class Fruit {
	private String name;
}

public class Main {
	public static void main(String[] args) {
		System.out.println(new Fruit());
	}
}

运行结果:Fruit@15db9742
  在之前的博文中,包括在上面我也有提到,当我们直接输出某个对象的时候,就会调用toString()方法。
  看一下println()的实现

public void println(Object x) {
    String s = String.valueOf(x);
    synchronized (this) {
        print(s);
        newLine();
    }
}

  思想就是要将对象用字符串的形式表示出来,然后输出(使用print()方法),并且加个回车符(newLine()方法)。
  valueOf()方法是用来return the string representation of the {@code Object} argument.它的实现就是

public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}

  正如我们所说的,归根结底还是调用了toString()方法。在本例中,我们并没有重写toString()方法,从运行结果我们可以看出本例中就是使用Object的toString()方法,即类名加上16进制的哈希码。这边就引出了一个很重要的点:

当创建一个类时,总是在继承,因此,除非已明确指出要从其他类中继承,否则就是隐式地从Java的标准根类Object进行继承。
                            ——《Java编程思想》

Object的源代码文档中也如是提到

Class {@code Object} is the root of the class hierarchy.Every class has {@code Object} as a superclass. All objects, including arrays, implement the methods of this class.

  

4.2 如果为Fruit类添加了toString()方法,那么使用System.out.println(new Fruit());调用了新增的toString方法。那么其父类中的toString方法的代码就没有了吗?如果同时想要复用其父类的toString方法,要怎么操作?(使用代码演示)

  原来父类的toString()方法并不会没有啊。一个爸爸可以有好几个儿子,就因为这个儿子重写了某个方法,导致其他儿子都得自己重写?显然是不可能的。只是对于父类中的方法,我既可以去使用它(不做改变)也可以按照自己的需求去修改它。Object类的toString()方法就是最好的例子,因为类名+哈希码的字符串表示在日常生活中应用不到,所以推荐每个类都要重写一个toString()方法。
  想在重写父类方法的同时复用父类的方法,只要用super关键字就好了,这边贴一个代码演示:

class Fruit {
	private String name;
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "Print in the subclass.\n" + super.toString();
	}
}

public class Main {
	public static void main(String[] args) {
		System.out.println(new Fruit());
	}
}

4.3 Fruit类还继承了Object类的eqauls方法。尝试分析其功能?自己编写一个equals方法覆盖父类的相应方法,功能为当两个Fruit对象name相同时(忽略大小写),那么返回true。(使用代码证明你自己覆盖的eqauls方法是正确的)

  equals()方法是用来“Indicates whether some other object is "equal to" this one.”
  equals()方法在Object类中的实现:

public boolean equals(Object obj) {
    return (this == obj);
}

  如源码所示,Object类中的equals()方法是比较引用的,即引用相同就返回true,不同返回false
  据说许多人都会把equals()方法写错,害怕,那我们尽量写的规范一点吧,首先这个 方法有5个要求:
  1. 自反性(reflexive):对于任何非空引用值 x,x.equals(x) 都应返回 true。
  2. 对称性(symmetric):对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
  3. 传递性(transitive):对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。
  4. 一致性(consistent):对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。
  5. 对于任何非空的引用x,x.equals(null)应该返回false
附上参考链接!
  
  而且,文档中还有说到:

Note that it is generally necessary to override the {@code hashCode} method whenever this method is overridden, so as to maintain the general contract for the {@code hashCode} method, which states that equal objects must have equal hash codes.

  就是要重写hashcode()这个方法……
  看上去很难,我本来想好好的思考一下怎么写的,但是自己太懒,想用eclipse生成hashcode()和equals()方法,然后再自己实现,没想到然后就??都做好了啊???那就不客气了,贴代码了,然后我来解释下吧:

class Fruit {
    private String name;
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "Print in the subclass.\n" + super.toString();
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Fruit other = (Fruit) obj;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equalsIgnoreCase(other.name))
			return false;
		return true;
	}
	public Fruit(String name) {
		super();
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

  这个自动生成的代码写的太好了。只可意会,不可言传。我也解释不了什么……
  hashcode()方法就是将一个Fruit对象hash成一个整数。这边是结合了String的hashcode()的方法。
  接下来重点介绍一下equals()方法:
  如果引用相同,那说明比较的是同一个对象,那么就返回true。满足自反性。
  非空的对象和null比较,返回false。满足第五条要求。
  两个对象的类要是相同的,类不同的话,相同无从谈起。这边用getClass()方法来代替instanceof,因为后者只是判断左边对象是否是右边类的实例,那如果左边是子类的对象,右边是父类,其实也是可以通过的,因为存在继承关系,so不好。在上面equals()方法附上的链接里面有详细的说明。
  还有要对name是否为空进行特判,上次在敲深克隆的代码时,因为没有注意到null,一直A不掉。结果试了一下,如果没有特判null,是会抛出异常的,所以以后都要注意的。后面就是返回字符串之间equals()的返回值了,没什么了。我唯一改了一下,就是要忽略大小写。
  
  
  

4.4 在4.3的基础上使用ArrayList fruitList存储多个fruit,要求如果fruitList中已有的fruit就不再添加,没有的就添加进去。请编写相关测试代码。并分析ArrayList的contatins方法是如何实现其功能的?

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ArrayList<Fruit> fruits = new ArrayList<Fruit>();
		Fruit[] fruits2 = new Fruit[6];
		fruits2[0] = new Fruit("apple");
		fruits2[1] = new Fruit("Apple");
		fruits2[2] = new Fruit("apqle");
		fruits2[3] = new Fruit("苹果");
		fruits2[4] = new Fruit("平果");
		fruits2[5] = new Fruit("apPle");
		for (int i = 0; i < fruits2.length; i++) {
			if (!fruits.contains(fruits2[i])) {
				fruits.add(fruits2[i]);
			}
		}
		for (Fruit fruit : fruits) {
			System.out.println(fruit.getName());
		}
	}

}

  测试结果如图所示:

  要分析ArrayList是如何实现contains()方法,就看看源码吧:

//contains()方法
public boolean contains(Object o) {
    return indexOf(o) >= 0;
}

//indexOf()方法
public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

  归根到底,还是要用到equals()方法,因为要查看是否有包含,就需要有对象的比较。正如各位所看到的,我们写的equals()方法是不区分大小写的,所以诸如Apple和apPle之类的都是不能加进这个ArrayList的。


5.代码阅读:PersonTest.java(abstract、多态)

5.1 画出类的继承关系

5.2 读懂main函数,将自己推测的出代码运行结果与真正运行结果进行比较。尝试分析原因

  真正的运行结果:

Manager [bonus=12000.3, toString()=Employee [salary=90000.1, toString()=Person [name=Clark, adress=GE, phonenumber=111, email=111@mail.com, age=10, gender=mail]]]
Student [status=1, toString()=Person [name=wang, adress=110, phonenumber=15959, email=15959@163.com, age=18, gender=male]]
Employee [salary=1000.0, toString()=Person [name=zhang, adress=136, phonenumber=1360, email=1360@mail.com, age=21, gender=female]]
Programmer [allowance=50000.0, toString()=Employee [salary=100000.0, toString()=Person [name=Gates, adress=usa, phonenumber=911, email=911@com, age=59, gender=male]]]

  分析原因:首先Person类是一个抽象类,所以根本就不存在可以实例化对象。因此这边只要分析Employee、Student、Manager和Programmer这四个类就行了。观察这四个类的toString()方法,我们可以发现他们具有统一的格式,就是输出自己特有的成员变量,然后调用父类的同名方法,对于Manager和Programmer这两个类,因为他们的父类Employee类也是继承Person类,所以他们会有两层嵌套。

5.3 子类中里面使用了super构造函数,作用是什么?如果将子类中的super构造函数去掉,行不行?

  作用是调用父类的构造器。
  不可以,因为如果将super构造器去掉,那么编译器会自动加入对父类无参构造器的调用,然而这个例子中没有一个类是有无参构造器的,所以会编译出错。我们会被要求“Must explicitly invoke another constructor”。所以即使无参构造器不会起到任何作用,我们还是最好能加上它,这样如果有其他类继承当前类,就可以在代码中隐式地调用这个什么都不做的构造器,然后显式地完成全新的初始化(就假如所有的成员变量都不需要用到父类的构造器就能够完成初始化)。

5.4 PersonTest.java中的代码哪里体现了多态?你觉得多态有什么好处?多态和继承有什么关系吗?

  首先要理解什么是多态,如果从广义的角度去理解,多态是一个很宽泛的概念。大致分为三种:
  1.特设多态(Ad hoc polymorphism),函数重载和运算符重载。
  2.参数化多态,就是把类型作为参数的多态。例如C++的模板。
  3.子类型化。

通过类继承机制和虚函数机制生效于运行期。可以优雅地处理异质对象集合,只要其共同的基类定义了虚函数的接口。也被称为子类型多态(Subtype polymorphism)或包含多态(inclusion polymorphism)。在面向对象程序设计中,这被直接称为多态。多态(计算机科学)来自维基

  所以我们这边就来聊聊OOP中的多态,就是子类型化。实现多态的方法叫做后期绑定,也叫做动态绑定运行时绑定。即编译器一直不知道对象的类型,但是方法调用机制能找到正确的方法体,并加以调用。这样我们就可以编写只与父类打交道的代码,并且这些代码对所有的子类都可以正确运行。
  我们来列举这边出现的多态:

  • System.out.println(person);/* Person类是一个抽象类,不能产生对象,所以也不会直接能够输出。但是我们并不知道到底输出的是属于哪个类的对象,直到运行的时候,才能知道,比如第0个就是Employee类的对象,就调用Employee类的toString()方法,第一个就是Student类的对象,就调用Student类的toString()方法。 */
  • 其他就没发现了,因为多态要和发送消息联系起来,也就是方法的调用,这边用父类调用方法的只有上述一个例子。

  多态的好处:
  1.可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
  2.可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
  3.接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如Circle和Sphere为了实现多态,完善或者覆盖这两个接口方法。
  4.灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
  5.简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。
网上都有比较统一的答案了,这边就不多说了
  简而言之,多态可以让我们更优雅地写出代码。
  
  多态和继承的关系:
  多态要用单一的接口去操作不同的对象。这边的接口不是专指Java中的interface,而是泛指对象上可以操作的方法。那么一个是interface的接口实现(后面再说),还有一种就是从父类继承的相同接口,然后使用该接口的不同形式,即不同版本的动态绑定方法。面向对象的三大特性:封装、继承和多态,可以说前面两个是为最后一个多态来服务的,而且多态不能单独来看,它只能作为类关系“全景”中的一部分,与其他特性协同工作。


3.使用码云管理Java代码


4.PTA实验

  • 4-1,有什么好说的吗?按部就班就好了。就是注意通过super来调用父类方法就好了。
  • 4-2,和书面作业的第五题其实是类似的,用父类开数组,然后每个元素用子类去实例化,最后输出,则是用到了多态,因为是通过父类的接口去实现子类的方法。
  • 4-3,首先要比较的是父类的属性,如果父类属性不等直接返回false好了,至于父类的equals()方法怎么写,就不关我们事了。其次就是要比较子类特有的company和salary两个变量。只要注意company可能为null,需要特判一下,其他按照提示做下来,就A掉了。
  • 4-4,这一题比较难,被卡了一会儿。这边需要介绍一下深克隆和浅克隆的区别。首先如果都是基本数据类型,不存在深克隆和浅克隆。那么如果是对象,那么将会复制引用,而不是对象的内容,这边拿一张图来做个分析:

    图片来源 csdn 详解Java中的clone方法 -- 原型模式
      在浅拷贝中,p1对p进行拷贝,但是p1的name域和p的name域都指向同一个字符串,这样就会存在一个问题,如果我对p的字符串进行修改,那么p1的字符串也会发生改变。所以我们需要深拷贝,就是复制对象的内容,而非引用。
      做这道题时有几个问题,首先clone()方法的返回值需要是当前类,比如本例就是Car类。其次就是需要判断对象是否为空,如果对象是空的话,就可以直接复制,这时候如果对null调用方法,就会抛出异常,所以我一开始一直都A不过去。
      然后就是要注意需要用clone()方法的类需要Cloneable的接口。这个编译器会自动加好的0.0
  • 5-4,就是按部就班按照提示去做就好了,没什么特别的地方,用到了抽象类和继承的思想。在父类当中定义好抽象的方法之后,在子类当中注意定义就好了。如果没有定义,将还是被视作是抽象类,也就是还是要加上abstract关键字。至于sumAllArea()和sumAllPerimeter()方法放在主类当中就好了,可以用。
  • 5-5,这道题就更没什么好说的了,将近一半的代码都是自动生成的。每一次加入都要判定之前是否已经存在同一个对象,就要涉及到equals()方法的撰写,稍微注意特判空就行了。
  • 5-6,之前我们用了比较器Comparator,这道题我是用的是Comparable接口,只要自己撰写compareTo()方法就好了,这道题代码比较长,不过都是按部就班地做下来,主要考察的是ArrayList的使用,用Collections.sort()来完成Arraylist的排序,今天早上做题目的时候楞了一下,忘记了。

看的不过瘾的请点下面
回到顶部


  做Java作业砸的时间挺多的,基本上交Java作业之前都是熬夜到1点左右,有点吃不消。下周开始就是密集的比赛,时间比较紧迫,所以可能会做的比较粗糙,望谅解。不过话说回来,好好地花时间研究,看《Java编程思想》,真的收获还是不小的。之前看一点就放弃了,觉得太艰深了,果然还是要结合自己在pta上做题的经验,然后再回过头看书,才有用。还有中英文维基百科结合起来一起用,也会学到很多。

posted @ 2017-03-16 18:38  学Java救不了中国人  阅读(890)  评论(7编辑  收藏  举报