201771010135杨蓉庆《面向对象程序设计(java)》第六周学习总结
实验六 继承定义与使用
1、实验目的与要求
(1) 理解继承的定义;
(2) 掌握子类的定义要求
(3) 掌握多态性的概念及用法;
(4) 掌握抽象类的定义及用途;
(5) 掌握类中4个成员访问权限修饰符的用途;
(6) 掌握抽象类的定义方法及用途;
(7)掌握Object类的用途及常用API;
(8) 掌握ArrayList类的定义方法及用法;
(9) 掌握枚举类定义方法及用途。
一、 理论学习部分目的与要求
5.1 类、超类和子类
1、父类/子类是相对的;
2、继承:用已有类来构建新类的一种机制;
3、继承层次结构中,每 个子类对象也可视作 是超类对象,因此, 可以将子类对象赋给 超类变量。
4、动态绑定和静态绑定 。
5、阻止继承:final类和方法
6、强制类型转换:如果要把一个超类对象赋给一个子类对象变量,就必须进 行强制类型转换。
7、受保护访问:如果希望超类的某些方法或域允许被子类访问,就需要在 超类定义时,将这些方法或域声明为protected 。
5.2 Object:所有类的超类
1、Object类是Java中所有类的祖先——每一个类都由它扩 展而来。在不给出超类的情况下,Java会自动把Object 作为要定义类的超类。
2、equals方法、hashCode方法、toString方法
5.3 泛型数组列表
1、Java中,利用ArrayList类,可允许程序在运行 时确定数组的大小(没有<>的ArrayList将被认为是一个删去了类型 参数的“原始”类型)
2、a.ArrayList定义
b.添加新元素
c.统计个数
d.调整大小
e.访问
f.增加与删除
5.4 对象包装器和自动打包
所有基本数据类型都有着与之对应的预定义类,它们被称 为对象包装器(wrapper)。
2、以下前6个对象包装器类都是从公共包装器类Number继承 而来。 Integer Long Float Double Short Byte Character Void Boolean
3、打包和拆包是编译器认可的。
5.5 参数数量可变的方法
用户自己可以定义可变参数的方法,并将参数指 定为任意类型,甚至是基本类型。
5.6 枚举类
1、声明枚举类
2、为枚举类增加构造函数
3、Enum类的API
5.7 继承设计的技巧
① 将公共操作和域放在超类。 ② 不要使用受保护的域。 ③ 使用继承实现“is-a”关系。 ④ 除非所有继承的方法都有意义,否则就不要 使用继承。 ⑤ 在覆盖方法时,不要改变预期的行为。 ⑥ 使用多态,而非类型信息。 ⑦ 不要过多地使用反射。
2、实验内容和步骤
实验1: 导入第5章示例程序,测试并进行代码注释
测试程序1:
Ÿ 在elipse IDE中编辑、调试、运行程序5-1 (教材152页-153页) ;
Ÿ 掌握子类的定义及用法;
结合程序运行结果,理解并总结OO风格程序构造特点,理解Employee和Manager类的关系子类的用途,并在代码中添加注释
package inheritance; /**
* This program demonstrates inheritance.
* @version 1.21 2004-02-21
* @author Cay Horstmann
*/
public class ManagerTest
{
public static void main(String[] args)
{
// construct a Manager object
Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);//Manager类定义类对象boss
boss.setBonus(5000);//更改器更改 Employee[] staff = new Employee[3];//生成雇员对象 // fill the staff array with Manager and Employee objects用Manager和Employee对象人员子类填充员工数组 staff[0] = boss;//子类对象
staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15); // print out information about all Employee objects
for (Employee e : staff)
System.out.println("name=" + e.getName() + ",salary=" + e.getSalary());
}
}
package inheritance; import java.time.*; public class Employee
{
private String name;
private double salary;
private LocalDate hireDay;//定义属性 public Employee(String name, double salary, int year, int month, int day)
{
this.name = name;
this.salary = salary;
hireDay = LocalDate.of(year, month, day);
}//构造器构造Employee对象 public String getName()
{
return name;
}//访问器 public double getSalary()
{
return salary;
} public LocalDate getHireDay()
{
return hireDay;
} public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
package inheritance; public class Manager extends Employee//扩展
{
private double bonus;//属性 /**
* @param name the employee's name
* @param salary the salary
* @param year the hire year
* @param month the hire month
* @param day the hire day
*/
public Manager(String name, double salary, int year, int month, int day)
{
super(name, salary, year, month, day);//调用的是父类参数
bonus = 0;
} public double getSalary()
{
double baseSalary = super.getSalary();//用父类的getSalary获得的
return baseSalary + bonus;
} public void setBonus(double b)//更改器
{
bonus = b;
}
}
及结果:
测试程序2:
Ÿ 编辑、编译、调试运行教材PersonTest程序(教材163页-165页);
掌握超类的定义及其使用要求;
Ÿ 掌握利用超类扩展子类的要求;
在程序中相关代码处添加新知识的注释。
package abstractClasses; /**
* This program demonstrates abstract classes.
* @version 1.01 2004-02-21
* @author Cay Horstmann
*/
public class PersonTest
{
public static void main(String[] args)
{
Person[] people = new Person[2];//定义一个Person对象数组 // fill the people array with Student and Employee objects
//用学生和雇员对象填充人员数组
people[0] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
people[1] = new Student("Maria Morris", "computer science"); // print out names and descriptions of all Person objects输出姓名和描述人员信息
for (Person p : people)
System.out.println(p.getName() + ", " + p.getDescription());//定义了getDescription方法
}
}
package abstractClasses; public abstract class Person
{
public abstract String getDescription();//abstract抽象方法
private String name; public Person(String name)//定义属性
{
this.name = name;//调用构造器构造参数
} public String getName()//访问器
{
return name;
}
}
package abstractClasses; public abstract class Person
{
public abstract String getDescription();//abstract抽象方法
private String name; public Person(String name)//定义name属性
{
this.name = name;//调用构造器构造参数
}
public String getName()//访问器
{
return name;
}
}
package abstractClasses; import java.time.*; public class Employee extends Person//扩展了Employee类
{
private double salary;//定义属性
private LocalDate hireDay;
//构造Employee类
public Employee(String name, double salary, int year, int month, int day)
{
super(name);
this.salary = salary;
hireDay = LocalDate.of(year, month, day);
}
//访问Salary属性;
public double getSalary()
{
return salary;
} public LocalDate getHireDay()
{
return hireDay;
} public String getDescription()
{
return String.format("an employee with a salary of $%.2f", salary);
}//表明是一个重写的方法 public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
package abstractClasses; public class Student extends Person//扩展了 Student类
{
private String major; /**
* @param nama the student's name
* @param major the student's major
*/
public Student(String name, String major)
{
// pass n to superclass constructor将n传递给超类构造函数
super(name);//父类构造器super
this.major = major;
} public String getDescription()
{
return "a student majoring in " + major;
}//访问器
}
测试程序3:
1、编辑、编译、调试运行教材程序5-8、5-9、5-10,结合程序运行结果理解程序(教材174页-177页);
2、掌握Object类的定义及用法;
3、 在程序中相关代码处添加新知识的注释。
Equals:
package equals; /**
* This program demonstrates the equals method.
* @version 1.12 2012-01-26
* @author Cay Horstmann
*/
public class EqualsTest
{
public static void main(String[] args)//定义方法
{
Employee alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15);
Employee alice2 = alice1;
Employee alice3 = new Employee("Alice Adams", 75000, 1987, 12, 15);
Employee bob = new Employee("Bob Brandson", 50000, 1989, 10, 1); System.out.println("alice1 == alice2: " + (alice1 == alice2)); System.out.println("alice1 == alice3: " + (alice1 == alice3)); System.out.println("alice1.equals(alice3): " + alice1.equals(alice3)); System.out.println("alice1.equals(bob): " + alice1.equals(bob)); System.out.println("bob.toString(): " + bob); Manager carl = new Manager("Carl Cracker", 80000, 1987, 12, 15);
Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
boss.setBonus(5000);
System.out.println("boss.toString(): " + boss);//返回
System.out.println("carl.equals(boss): " + carl.equals(boss));
System.out.println("alice1.hashCode(): " + alice1.hashCode());
System.out.println("alice3.hashCode(): " + alice3.hashCode());
System.out.println("bob.hashCode(): " + bob.hashCode());
System.out.println("carl.hashCode(): " + carl.hashCode());
}
}
Employee.java:
package equals; import java.time.*;
import java.util.Objects; public class Employee
{
private String name;
private double salary;//访问两个对象
private LocalDate hireDay;
//构造方法
public Employee(String name, double salary, int year, int month, int day)
{
this.name = name;
this.salary = salary;//this调用
hireDay = LocalDate.of(year, month, day);
}
//返回一个代表该对象域值的字符串
public String getName()
{
return name;
} public double getSalary()
{
return salary;
} public LocalDate getHireDay()
{
return hireDay;
} public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
} public boolean equals(Object otherObject)
{
// a quick test to see if the objects are identical
if (this == otherObject) return true; // must return false if the explicit parameter is null
if (otherObject == null) return false; // if the classes don't match, they can't be equal
if (getClass() != otherObject.getClass()) return false; // now we know otherObject is a non-null Employee
Employee other = (Employee) otherObject; // test whether the fields have identical values
return Objects.equals(name, other.name) && salary == other.salary && Objects.equals(hireDay, other.hireDay);
} public int hashCode()
{
return Objects.hash(name, salary, hireDay);
}
//调用超类的toString方法
public String toString()
{
return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay
+ "]";
}
}
Manger.java:
package equals;
//定义Manger方法
public class Manager extends Employee
{
private double bonus; public Manager(String name, double salary, int year, int month, int day)
{
super(name, salary, year, month, day);
bonus = 0;
} public double getSalary()//访问器
{
double baseSalary = super.getSalary();
return baseSalary + bonus;
} public void setBonus(double bonus)
{
this.bonus = bonus;//this的调用
} public boolean equals(Object otherObject)//比较两个对象是否相等
{
if (!super.equals(otherObject)) return false;
Manager other = (Manager) otherObject;
// super.equals checked that this and other belong to the same class
return bonus == other.bonus;
} public int hashCode()
{
return java.util.Objects.hash(super.hashCode(), bonus);
}//返回对象的散列码 public String toString()
{
return super.toString() + "[bonus=" + bonus + "]";
}//返回描述该对象值的字符串
}
结果如下:

测试程序4:
Ÿ 在elipse IDE中调试运行程序5-11(教材182页),结合程序运行结果理解程序;
Ÿ 掌握ArrayList类的定义及用法;
在程序中相关代码处添加新知识的注释。
package arrayList; import java.util.*; import equals.Employee; /**
* This program demonstrates the ArrayList class.
* @version 1.11 2012-01-26
* @author Cay Horstmann
*/
public class ArrayListTest
{
public static void main(String[] args)
{
// fill the staff array list with three Employee objects
ArrayList<Employee> staff = new ArrayList<>();
//用add方法为数组添加新元素
staff.add(new Employee("Carl Cracker", 75000, 1987, 12, 15));
staff.add(new Employee("Harry Hacker", 50000, 1989, 10, 1));
staff.add(new Employee("Tony Tester", 40000, 1990, 3, 15)); // raise everyone's salary by 5%
for (Employee e : staff)
e.raiseSalary(5); // print out information about all Employee objects
for (Employee e : staff)
System.out.println("name=" + e.getName() + ",salary=" + e.getSalary() + ",hireDay="
+ e.getHireDay());
}//循环数组
}
结果如下:

测试程序5:
1、 编辑、编译、调试运行程序5-12(教材189页),结合运行结果理解程序;
2、 掌握枚举类的定义及用法;
在程序中相关代码处添加新知识的注释。
package enums; import java.util.*; /**
* This program demonstrates enumerated types.
* @version 1.0 2004-05-24
* @author Cay Horstmann
*/
public class EnumTest
{
public static void main(String[] args)
{
//构造枚举常数
Scanner in = new Scanner(System.in);
System.out.print("Enter a size: (SMALL, MEDIUM, LARGE, EXTRA_LARGE) ");
String input = in.next().toUpperCase();
Size size = Enum.valueOf(Size.class, input);//静态方法valueOf
System.out.println("size=" + size);
System.out.println("abbreviation=" + size.getAbbreviation());
if (size == Size.EXTRA_LARGE)
System.out.println("Good job--you paid attention to the _.");
}
}
//声明定义类型是一个类
enum Size
{
SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
//构造器
private Size(String abbreviation) { this.abbreviation = abbreviation; }
public String getAbbreviation() { return abbreviation; } private String abbreviation;//返回枚举常量名
}
结果如下:

实验2:编程练习1
定义抽象类Shape:
属性:不可变常量double PI,值为3.14;
方法:public double getPerimeter();public double getArea())。
让Rectangle与Circle继承自Shape类。
编写double sumAllArea方法输出形状数组中的面积和和double sumAllPerimeter方法输出形状数组中的周长和。
main方法中
1)输入整型值n,然后建立n个不同的形状。如果输入rect,则再输入长和宽。如果输入cir,则再输入半径。
2) 然后输出所有的形状的周长之和,面积之和。并将所有的形状信息以样例的格式输出。
3) 最后输出每个形状的类型与父类型,使用类似shape.getClass()(获得类型),shape.getClass().getSuperclass()(获得父类型);
思考sumAllArea和sumAllPerimeter方法放在哪个类中更合适?
输入样例:

输出样例:

package TEST; import TEST.Shape;
import TEST.Rectangle;
import TEST.Circle;
import java.math.*;
import java.util.Scanner;
public class demo { public static void main(String[] args) {
Scanner in=new Scanner(System.in);
System.out.println();
int n=in.nextInt();
String rect="rect";
String cir="cir";
Shape [] a=new Shape[n];
for(int i=0;i<n;i++) {
String read=in.next();
if(read.equals(rect)) {
int length=in.nextInt();
int width=in.nextInt();
a[i]=new Rectangle(width,length);
}
if(read.equals(cir)) {
int radius=in.nextInt();
a[i]=new Circle(radius);
}
}
for(int i=0;i<n;i++)
{
System.out.println(a[i]);
} demo b=new demo();
System.out.println(b.sumAllPerimeter(a));
System.out.println(b.sumAllArea(a));
for(Shape s:a) {
System.out.println(s.getClass()+","+s.getClass().getSuperclass());
}
}
public double sumAllArea(Shape a[])
{
double sum=0;
for(int i=0;i<a.length;i++)
sum=sum+a[i].getArea();
return sum;
}
public double sumAllPerimeter(Shape a[])
{
double sum=0;
for(int i=0;i<a.length;i++)
sum=sum+a[i].getPerimeter();
return sum;
}
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("请输入创建图形的个数");
int a = in.nextInt();
System.out.println("请输入图形种类(选择输入cir或rect)");
String rect="rect";
String cir="cir";
Shape[] num=new Shape[a];
for(int i=0;i<a;i++){
String input=in.next();
if(input.equals(rect)) {
System.out.println("请输入长和宽");
int width = in.nextInt();
int length = in.nextInt();
num[i]=new 长方形(width,length);
System.out.println("长方形["+"长方形的长为:"+length+" 长方形的宽为:"+width+"]");
}
if(input.equals(cir)) {
System.out.println("输入所创建的圆的半径");
int r = in.nextInt();
num[i]=new 圆形(r);
System.out.println("圆["+"圆的半径为:"+r+"]");
}
}
cirec c=new cirec();
System.out.println("求所有图形的面积和:");
System.out.println(c.AllPerimeter(num));
System.out.println("求所有图形的周长和:");
System.out.println(c.AllArea(num));
for(Shape s:num) {
System.out.println(s.getClass()+","+s.getClass().getSuperclass());
}
}
}
package 两个图形的;
abstract class Shape {
abstract double getPerimeter();
abstract double getArea();
}
class 圆形extends Shape{
private double PI=3.14;
private int r;
public 圆形(int r) {
this.r = r;
}
double getPerimeter(){
return 2 * PI * r;
}
double getArea(){
return r * r*PI;
}
}
class 长方形extends Shape{
private int width;
private int length;
public 长方形(int length, int width) {
this.width = width;
this.length = length;
}
double getPerimeter(){
return 2*(length+width);
}
double getArea(){
return length*width;
}
}
结果:

实验3: 编程练习2
编制一个程序,将身份证号.txt 中的信息读入到内存中,输入一个身份证号或姓名,查询显示查询对象的姓名、身份证号、年龄、性别和出生地
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Scanner; public class ID { public static People findPeopleByname(String name) {
People flag = null;
for (People people : peoplelist) {
if(people.getName().equals(name)) {
flag = people;
}
}
return flag; } public static People findPeopleByid(String id) {
People flag = null;
for (People people : peoplelist) {
if(people.getnumber().equals(id)) {
flag = people;
}
}
return flag; } private static ArrayList<People> peoplelist; public static void main(String[] args) {
peoplelist = new ArrayList<People>();
Scanner scanner = new Scanner(System.in);
File file = new File("D:\\java\\1\\身份证号.txt");
try {
FileInputStream files = new FileInputStream(file);
BufferedReader in = new BufferedReader(new InputStreamReader(files));
String temp = null;
while ((temp = in.readLine()) != null) { Scanner linescanner = new Scanner(temp);
linescanner.useDelimiter(" ");
String name = linescanner.next();
String number = linescanner.next();
String sex = linescanner.next();
String age = linescanner.next();
String place = linescanner.nextLine();
People people = new People();
people.setName(name);
people.setnumber(number);
people.setage(age);
people.setsex(sex);
people.setaddress(address);
peoplelist.add(people); }
} catch (FileNotFoundException e) {
System.out.println("文件未找到");
e.printStackTrace();
} catch (IOException e) {
System.out.println("文件读取错误");
e.printStackTrace();
}
boolean isTrue = true;
while (isTrue) { System.out.println("***********");
System.out.println("1.按姓名查询");
System.out.println("2.按身份证号查询");
System.out.println("3.退出");
System.out.println("***********");
int nextInt = scanner.nextInt();
switch (nextInt) {
case 1:
System.out.println("请输入姓名:");
String peoplename = scanner.next();
People people = findPeopleByname(peoplename);
if (people != null) {
System.out.println(" 姓名:"+ people.getName() +
" 身份证号:"+ people.getnumber() +
" 年龄:"+ people.getage()+
" 性别:"+ people.getsex()+
" 地址:"+ people.getadress()
);
} else {
System.out.println("不存在此人");
}
break;
case 2:
System.out.println("请输入身份证号:");
String peopleid = scanner.next();
People people1 = findPeopleByid(peopleid);
if (people1 != null) {
System.out.println(" 姓名:"+ people1.getName()+
" 身份证号:"+ people1.getnumber()+
" 年龄:"+ people1.getage()+
" 性别:"+ people1.getsex()+
" 地址:"+ people1.getplace());
} else {
System.out.println("不存在此人");
}
break;
case 3:
isTrue = false;
System.out.println("byebye!");
break;
default:
System.out.println("输入有误");
}
}
}}
结果如下:

总结:通过本章继承的学习 ,我明白了to string、子类、超类、枚举类等理论知识,因为国庆出去一趟,也没怎么看书,回来后草草的完成了作业,实验部分自己动手呢能力不是很强,还得努力学习。
201771010135杨蓉庆《面向对象程序设计(java)》第六周学习总结的更多相关文章
- 201771010134杨其菊《面向对象程序设计java》第九周学习总结
第九周学习总结 第一部分:理论知识 异常.断言和调试.日志 1.捕获 ...
- 201871010132-张潇潇《面向对象程序设计(java)》第一周学习总结
面向对象程序设计(Java) 博文正文开头 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cn ...
- 扎西平措 201571030332《面向对象程序设计 Java 》第一周学习总结
<面向对象程序设计(java)>第一周学习总结 正文开头: 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 ...
- 杨其菊201771010134《面向对象程序设计Java》第二周学习总结
第三章 Java基本程序设计结构 第一部分:(理论知识部分) 本章主要学习:基本内容:数据类型:变量:运算符:类型转换,字符串,输入输出,控制流程,大数值以及数组. 1.基本概念: 1)标识符:由字母 ...
- 201871010124 王生涛《面向对象程序设计JAVA》第一周学习总结
项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://edu.cnblogs.com/campus/xbsf/ ...
- 201871010115——马北《面向对象程序设计JAVA》第二周学习总结
项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...
- 201777010217-金云馨《面向对象程序设计(Java)》第二周学习总结
项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...
- 201871010132——张潇潇《面向对象程序设计JAVA》第二周学习总结
项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...
- 201771010123汪慧和《面向对象程序设计Java》第二周学习总结
一.理论知识部分 1.标识符由字母.下划线.美元符号和数字组成, 且第一个符号不能为数字.标识符可用作: 类名.变量名.方法名.数组名.文件名等.第二部分:理论知识学习部分 2.关键字就是Java语言 ...
- 201521123061 《Java程序设计》第六周学习总结
201521123061 <Java程序设计>第六周学习总结 ***代码阅读:Child压缩包内 1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核 ...
随机推荐
- (c#)拼写单词
题目 解
- Mysql SQL CAST()函数
(1).CAST()函数的参数是一个表达式,它包括用AS关键字分隔的源值和目标数据类型.以下例子用于将文本字符串'12'转换为整型: SELECT CAST('12' AS int) (2).返回值是 ...
- ubuuntu截图
方法1: 按 print screen sysrq 方法2: 系统设置 选择键盘 选择快捷键窗口 选择截图 按照自己的习惯更改快捷键即可.
- JSON--WEB SERVICE
Query ajax webservice:get 和 post 一.GET 方式 客户端 复制代码代码如下: var data = { classCode: "0001"}; / ...
- 忘记win8开机密码的清除方法
1.进PE 2.打开计算机,进入 C:\Windows\System32 目录下 3.找到 magnify.exe 改名为 mangify1.exe .将 cmd.exe 改名为 magnify.ex ...
- 【资源分享】半条命2速通AHK脚本
*----------------------------------------------[下载区]----------------------------------------------* ...
- JavaScript对象,函数,作用域
JavaScript对象 在 JavaScript中,几乎所有的事物都是对象.JavaScript 对象是拥有属性和方法的数据. var car = {type:"Fiat", m ...
- 【读书笔记】--《编写高质量iOS与OS X代码的52个有效方法》
1.Objective-C 起源: 在 C 语言基础上添加了面向对象特性,是 C 语言的超集.Objective-C 由 SmallTalk 语言演变过来,使用消息结构,运行环境由运行环境决定. OC ...
- PHP基础学习笔记5
一.连接MYSQL 1.1 MySQLi - 面向对象 <?php $servername = "localhost"; $username = "username ...
- 论STA | 工艺、工具、分析的本与末
人类从漫长的蒙昧中觉醒之后,不再依靠着奇装异服的巫师通灵来指导生活,巫师进化成了科学家,他们试图对周遭的一切进行概括.分类.抽象,于是有了化学.物理.数学等基科.比如一粒沙,它的化学组成是什么,物理特 ...