面向对象高级特性:
不可变对象:
一般的对象是在创建之后可以改变它的属性,但有时候也会需要在创建一个对象后其内容就不可以再改变,这种对象就是不可变对象,而与之对应的类就是不可变类
String 类就是一个不可变类
如果一个类是不可变的,那么它的所有数据域必须都是私有的而且没有对任何一个数据域提供set方法
import java.util.Date;
publicclass Student {
// 不可变类所有的属性都是私有的
privateintid;
private String name;
private Date dateCreated;
// 对于不可变类要使用构造方法以提供初始值
public Student(int sn, String newName) {
id = sn;
name = newName;
}
// 不可变类只提供get方法
publicint getId() {
returnid;
}
public String getName() {
returnname;
}
public Date getDateCreated() {
returndateCreated;
}
}
上面这个Student类就是一个典型的不可变类,但是注意这里面我们有一个Date的引用变量这个属性不是不可变对象因而这个值是可以变的
dateCreated.setTime(2000000);可以如同这样进行改变
变量:
一个类中的实例变量和静态变量称为类变量或数据域,在一个方法中定义的变量称为局部变量。不管在何处进行声明,类变量的作用域是整个类,类的变量和方法在类中可以在任意位置出现,但是有一点不一样就是当一个数据域是基于另一个数据域的引用来进行初始化的话则要先把这个另一个数据域先声明,因为要先声明才可以使用
类变量是只能声明一次的,如果一个局部变量与一个类变量有相同的名字则局部变量是优先的也就是如果在一个方法中声明了一个与类中的属性相同名的变量,则在方法中通过这个名字访问的是这个方法中的局部变量
this引用:
this指向的是调用对象本身的引用名,用它来常常去调用类中的隐藏域,比如方法中的参数名与类中的属性名同明的时候可以加上this.来明确表示引用类中的属性
关键字this给出一种指代对象的方法
this的另一个用法是在一个构造器中去调用另一个构造方法比如:this(实参)这个表示调用一个具有什么参数的构造方法,这个是在一个构造方法中去明确调用的如果去调用了只能放在这个构造方法的第一行去调用。
类的抽象封装
类的抽象是把类的实现与使用分离。类的创建者提类的描述,告诉使用都如何去使用一个类。
类的封装:类的使用者不需要知道类是如何实现的,只需知道怎么去使用则可以这就是对类的一种封装
构建一个类是为了在更大的应用范围内使用它,类应该通过构造方法、属性和方法提供不同方式进行定制,一般会提供多个构造方法,多个set、get方法
如果我们使用面向过程的方法来思考的话也可以把多个值作为一种属性放在一起但它们之间没有紧密的耦合在一起,要把它们耦合在一起最为理想的方法是创建一个类并声明出相应的对象
比如下面的一个类进行身体BMI测试:
publicclass BMI {
// 属性
private String name;
privateintage;
privatedoubleweight;
privatedoubleheight;
// 构造方法
public BMI(String name, int age, double weight, double height) {
this.name = name;
this.age = age;
this.weight = weight;
this.height = height;
}
public BMI(String name, double weight, double height) {
this(name, 20, weight, height);
}
// 行为
publicdouble getBMI() {
double bmi = weight / height / height;
// 返回的结果保留两位小数
return Math.round(bmi * 100) / 100.0;
}
public String getStatus() {
double bmi = getBMI();
if (bmi < 16)
return"太轻了!";
elseif (bmi < 18)
return"偏轻!";
elseif (bmi < 24)
return"正常!";
elseif (bmi < 29)
return"偏胖!";
elseif (bmi < 35)
return"过胖!";
else
return"太胖了!";
}
// 提供相应的get、set方法
public String getName() {
returnname;
}
publicvoid setName(String name) {
this.name = name;
}
publicint getAge() {
returnage;
}
publicvoid setAge(int age) {
this.age = age;
}
publicdouble getWeight() {
returnweight;
}
publicvoid setWeight(double weight) {
this.weight = weight;
}
publicdouble getHeight() {
returnheight;
}
publicvoid setHeight(double height) {
this.height = height;
}
}
import java.text.SimpleDateFormat;
import java.util.Date;
publicclass TestBMI {
publicstaticvoid main(String[] args) {
BMI bmi = new BMI("谢声", 28, 59, 1.69);
System.out.println("测试时间:"
+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new Date()));
System.out.println(bmi.getName() + " 你的BMI值:" + bmi.getBMI()
+ " 当前的身体状态:" + bmi.getStatus());
}
}
对象的组合:
一个对象可以包含另一个对象,这两个对象之间的关系称为组合比如说上面的BMI类中有一个String数据域,这就是一个组合关系
如果是一对一的关系则是一种组合,如果是一对多的关系就是一种聚集关系,组合是一种特殊的聚集关系比如学生只有一个String类型的Id号则是一种组合,学生可以有多个地址Address这是一种聚集
对于聚集可以使用一个对象数组来存储
设计一个经典的Course类:
这是一个课程信息,每门课程都要有一个名字及选课的学生,要满足可以从这个课程中添加和删除学生
构造方法:Course(String name);通过传一个课程名来构造一个课程对象,可以使用addStudent(Student stu)方法来向某门课程加入学生,deleteStudent(student stu)方法来删除一个学生
import java.util.Arrays;
publicclass Course {
/** 学生对象数组 */
private Student[] stu = new Student[0];
/** 课程名 */
private String name;
/** 学生的人数 */
privateintnumberOfStu;
// 构造方法
public Course(String name) {
this.name = name;
}
// 添加一个学生进来
publicvoid addStudent(Student stu) {
this.stu = Arrays.copyOf(this.stu, this.stu.length+1);
this.stu[numberOfStu] = stu;
numberOfStu++;
}
// 添加一组学生进来
publicvoid addStudents(Student[] stu) {
for (int i = 0; i < stu.length; i++)
addStudent(stu[i]);
}
// 删除一个学生,使用学生的对象来删除
publicvoid deleteStudent(Student stu) {
int id = stu.getId();// 得到要删除学生的ID
for (int i = 0; i < this.stu.length; i++) {
if (this.stu[i].getId() == id) {
if (i == this.stu.length - 1)
this.stu = Arrays.copyOf(this.stu, this.stu.length - 1);
else {
for (int j = i; j < this.stu.length - 1; j++)
this.stu[j] = this.stu[j + 1];
System.arraycopy(this.stu, 0, this.stu, 0,
this.stu.length - 1);
this.stu = Arrays.copyOf(this.stu, this.stu.length - 1);
}
numberOfStu--;
return;
}
}
}
publicclass TestCourse {
publicstaticvoid main(String[] args) {
// 先新建学生数组
Student[] stu = new Student[] { new Student(1, "xiaoxie"),
new Student(2, "advent"), new Student(3, "谢声") };
// 创建一个课程对象
Course course = new Course("Computer");
// 把一组学生加入进来
course.addStudents(stu);
// 打印结果
System.out.println("当前学生人数:" + course.getNumberOfStu());
System.out.println(course.getStu().length);
// 删除一个学生
course.deleteStudent(2);
// 删除后的结果
System.out.println("当前学生人数:" + course.getNumberOfStu());
for (int i = 0; i < course.getNumberOfStu(); i++) {
System.out.println("id: "+course.getStu()[i].getId()+" 姓名:"+course.getStu()[i].getName());
}
System.out.println(course.getStu().length);
}
}
设计堆栈类:
栈:后进先出LIFO
Data2 |
Data1 |
Data1 |
Data1 |
封装一个栈的类并提供相应的处理方法
import java.util.Arrays;
publicclass StackOfInteger {
// 属性
privateint[] elements;
privateintsize;
publicstaticfinalintDEFAULT_CAPACITY = 16;
// 构造方法:
public StackOfInteger() {
elements = newint[DEFAULT_CAPACITY];
}
public StackOfInteger(int capacity) {
elements = newint[capacity];
}
// 提供入栈方法
publicvoid push(int value) {
if (size >= elements.length)
elements = Arrays.copyOf(elements, elements.length * 2);
elements[size++] = value;
}
// 出栈方法
publicint pop() {
returnelements[--size];
}
// 返回将要出栈的元素
publicint peek() {
returnelements[size - 1];
}
// 判断这个栈是否为空
publicboolean isEmpty() {
returnsize == 0;
}
// 返回当前栈中元素的个数
publicint getSize() {
returnsize;
}
}
publicclass TestStack {
publicstaticvoid main(String[] args) {
StackOfInteger si = new StackOfInteger();
for(int i=0;i<=20;i++)
si.push(i);
System.out.println( "栈中元素个数:"+si.getSize());
//出栈结果
System.out.println("栈中元素依次出栈结果");
while(!si.isEmpty())
System.out.print(si.pop()+" ");
}
}
类的设计原则:
内聚:类应该是描述一个单一的实体,所有类的操作在逻辑上相互配合,可以为一个学生创建一个类而不应该把学生与老师组合在一个类当中,因为它们是不同的实体。
一致:编写类的时候按一定的风格比比如:数据在构造方法前,构造方法在方法之前,而且命名要有一定的信息量包含在其名字中而且注意一般是一定要提供无参的公共构造方法。
封装:数据应使用private来进行隐藏,如果数据可读则提供get方法,可写则提供set方法
清晰:类中的逻辑要容易理解和解释比如我们看成一下JDK中String类方法subString(int startIndex,int endIndex)取的是startIndex到endIndex-1这个范围的子串这个让人在理解上有点混乱。而且在设计类的时候对于数据域不要重复进行声明比如数据域中有一个引用其它的对象而这个引用的对象有了相应的数据这时在这个类当中就不应该再声明这样一个同样的数据域了因为相应的数据可以从引用对象中得到
完整:类应该可以在多个不同的应用中使用,一个类应该通过属性和方法来使用它适应用户的不同的需求
实例和静态:如果依赖于类的具体实例的变量和方法则应该是一个实例变量或方法而如果一个变量被类的所有实例共享则应该是静态的。比如构造方法应该是永远都是实例方法一个静态变量或方法可以从实例方法中调用而不能从静态方法中调用实例变量或方法!