目录
6、call by value 和call by reference
1、类和对象
由类构造 (construct ) 对象的过程称为创建类的实例 (instance ) .
使用 OOP,—定要清楚对象的三个主要特性:
•对象的行为(behavior)—可以对对象施加哪些操作,或可以对对象施加哪些方法?
•对象的状态 (state)—当施加那些方法时,对象如何响应?
•对象标识(identity )—如何辨别具有相同行为与状态的不同对象?
类的最常见(符号也最好辨认)的关系有:
•依赖 (“ uses-a”)
•聚合(“ has-a”)
•继承(“ is-a”)
2、封装
封装 ( encapsulation , 有时称为数据隐藏)是与对象有关的一个重要概念。 从形式上看,封装不过是将数据和行为组合在一个包中,并对对象的使用者隐藏了数据的实现方式。对象中的数据称为实例域,操作数据的过程称为方法。
关键在于绝对不能让类中的方法直接地访问其他类的实例域。 程序仅通过对象的方法与对象数据进行交互。封装给对象赋予了“ 黑盒” 特征, 这是提高重用性和可靠性的关键。
一个私有数据源
一个公有域访问器
一个公有域更改器 可以防止某些地方邪恶的更改了数据源,可以做一些更改前的检查。
3、预定义类
例如 Math,Date类(表示时间点)、localDate类(日历表示法)。
封装了方法而不用看到具体的实现形式。 localDate不再是通过构造器来,而是静态工厂方法来进行。
localDate.now();
LocalDate newYearsEve = LocalDate.of(1999, 12, 31);
int year = newYearsEve.getYearO; // 1999
int month = newYearsEve.getMonthValueO; // 12
int day = newYearsEve.getDayOfMonth(); // 31
LocalDate newYearsEve = newYearsEve.piusDays(1000);
注意:不要返回引用可变的对象的访问器方法。看下面的代码
class Employee
{
private Date hireDay ;
public Date getHireDay{
return hireDay; // Bad
}
}
localDate这个改变值的方法,但Date类有一个setTime方法,它可以改变引用的值,这样就破坏了封装。
Employee em = new Employee()
Date d = em.getHireDay();
double tenYearsInMilliSeconds = 10 * 365.25 * 24 * 60 * 60 * 1000;
d.setTime(d.getTime()-(long)tenYearsInMilliSeconds));
此时d和em.getHireDay()是引用的是同一个对象,d.setTime()的方法改变了Employee的私有数据域。
如果需要返回一个可变对象的引用,首先对它进行克隆。对象clone是指存放在另一个位置上的对象的复本。
class Employee{
.....
public Date getHireDay(){
return (Date)hireDay.clone();
}
}
4、继承
可以通过扩展一个类来建立另外一个新的类。
5、静态域与静态方法
a、final修饰符大都应用于基本(primitive)类型域,或不可变(immutable)类的域(如果类中的每个方法都不会改变其对象,这种就是不可变的类,比如String类)
对于可变对象用final修饰容易造成读者的误解
Employee(){
private final StringBuilder evaluations;
}
evaluations指向的这个变量不会被其他的对象所引用
但是这个可以改变:evaluations.append(localDate.new()+"ending");
b、static静态域
实例域和静态域 静态域属于类不属于对象
class employee(){
private String uuid;//对于实例域,每一个实例有一个自己的拷贝
private static String taskId;//每一个实例共同拥有同一个域
}
b.1 静态常量
public class System
{
public static final PrintStream out = . . .;
}
在类中的静态常量设置为public也并没有什么不妥,因为已经防止了其他的方法改变他的情况
…
b.2静态方法
静态方法是不能对对象进行操作的方法,如Math.pom(x,a)计算的是x的a次方、程序入口的mian方法、 同样的静态方法是对类的操作不是对对象的操作,上图中的 Employee中的 public static getTaskId(){ return taskId;}这个不能对id进行操作。
6、call by value 和call by reference
java程序设计语言总是通过call by call,方法得到的是所有参数的拷贝。方法不会修改传递给它的任何参数。
a、参数中的基础数据类型是call by value,对象是call by reference
double percent = 10;
harry.raiseSalary(percent):
b、参数中的基础数据类型是对象,
public static void tri pi eSalary(Employee x) // works
{
x.raiseSa1ary(200) ;
}
下面总结一下 Java 中方法参数的使用情况:
• 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。
• 一个方法可以改变一个对象参数的状态。
• 一个方法不能让对象参数引用一个新的对象。
7、对象构造
一、构建对象时域的初始化 构建对象,就是用new class()语句建立一个新的类的对象。在这种情况下,类中的域是按照如下顺序进行初始化的:赋予默认值-->(静态域、静态块)-->(实例域、实例块)-->构造器。
a、初始化块
前面已经讲过两种初始化数据域的方法:
•在构造器中设置值
•在声明中赋值
实际上,Java 还有第三种机制, 称为初始化块(initializationblock)。
class Employee
{
private static int nextld;
private int id;
private String name;
private double salary;
// object initialization block
{
id = nextld;
nextld++;
}
public Employee(String name, double salary){
this.name = name;
this.salary = salary;
}
}
java中域的初始化 这部分摘自 https://www.cnblogs.com/jerry007/archive/2013/01/18/java%E4%B8%AD%E5%9F%9F.html
初始化域即给域赋值有以下几种方式:1)、赋予默认赋值 2)、声明变量时同时赋值 3)、块赋值(实例块和静态块) 4)、构造器赋值
如果同时存在以上几种赋值方式,那么域的最终值会是哪个呢?这里就涉及到域的初始化顺序的问题,同时也是下面要讲到的问题。 我们把域的初始化分为两种情况, 一种是在建立对象即进行类的实例化时域的初始化;另一种是在不建立对象,只装载类的时候域的初始化。
一、构建对象时域的初始化 构建对象,就是用new class()语句建立一个新的类的对象。在这种情况下,类中的域是按照如下顺序进行初始化的:赋予默认值-->(静态域、静态块)-->(实例域、实例块)-->构造器。 下面我给解释一下,假设一个域即变量int a,当建立对象时,首先赋予它一个默认值,int类型的默认值为0;如果a为静态域并且在静态块中被赋值,那么就按照静态域和静态块在程序中出现的顺序先后执行;如果同时还在实例块中被赋值,则再执行实例块中的赋值语句(静态域不可能再是实例域);最后执行构造器中的赋值语句(如果在构造器中有被赋值的话)。如果变量a是实例域,则不会有静态域和在静态块中赋值(不能在静态块中给实例域赋值)的情况,其他同前所述。 总结:静态域是指静态域在声明时被赋值,静态块是指在静态块中被赋值,实例域和实例块同上。上面列出的四个步骤是先后执行的;括号中的两种方式是同等级的,按照在程序中的顺序执行,排在前面的先执行,排在后面的后执行。
二、装载类时域的初始化 有两种情况是只装载类而不实例化类,一是用java classname执行程序时;二是用classname.statement调用类的静态域或静态方法时。 装载类时这个类并没有被实例化,也就不能形成对象,所以不能对实例域进行初始化。因此只有静态域、静态块才能被初始化执行,执行规则同构建对象时的规则。
b、显式域初始化
在执行构造器之前,先执行赋值操作。当一个类的所有构造器都希望把相同的值赋予某个特定的实例域时,这种方式特别有用。
class Employee
{
private static int nextld;
private int id = assignld();
private static int assignld() {
int r = nextld;
nextld++;
return r;
}
}