第四章 - 对象和类
4.2.3 更改器和访问器方法
更改器方法(mutator method)**
如GregorianCalendar.add()会改变原有对象的状态:
GregorianCalendar someDay = new GregorianCalendar(1999,11,31);
someDay.add(Calendar.DAY_OF_MONTH, 1000); //someDay会改变
访问器方法(accessor method)
只访问对象而不去修改对象的方法,例如:LocalDate.getYear() 和 toUpperCase()
4.3.1 Employee 类
import java.time.LocalDate;
/**
* This program tests the Employee class.
*/
public class EmployeeTest {
public static void main(String[] args){
Employee[] staff = new Employee[3];
staff[0] = new Employee("Warren Song",75000,1999,12,15);
staff[1] = new Employee("Matjek Chen",50000,1998,10,15);
staff[2] = new Employee("Chris Li",30000,1995,8,15);
for(Employee e : staff){
e.raiseSalary(5);
}
for (Employee e : staff){
System.out.println("name = "+e.getName()+", salary = "+e.getSalary()+", hireDay = "+ e.getHireDay());
}
}
}
class Employee{
private String name;
private double salary;
private LocalDate hireDay;
//constructor
public Employee(String n, double s, int year, int month, int day){
name = n;
salary = s;
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;
}
}
4.3.9 final 实例域
final 在创建后,这个值不会再被修改
class Employee{
private final String name;
. . .
}
- 用来修饰一个引用
- 用来修饰一个方法
这个方法将成为最终方法,无法被子类重写。但可以被继承
- 用来修饰类
用final修改类时,该类成为最终类,无法被继承。简称为“断子绝孙类”
final class Person02{}
//class Teacher02 extends Person02{} 无法继承
4.4.3 静态方法
属于类且不属于类对象的变量和函数
/ —待完善— /
4.5 方法参数
按值调用 call by value
引用调用 call by reference
JAVA程序设计语言对对象采用的不是引用调用,实际上,对象引用的是值传递。**
- 一个方法不能修改一个基本数据类型的参数(数值型和布尔型)
- 一个方法可以改变一个对象参数的状态( x.raiseSalary(200) )(对对象参数的修改保留了下来)
- 一个方法不能让对象参数引用一个新的对象
例如swap(a,b)方法:**
public static void swap(Employee x,Employee y){
Employee temp=x;
x = y;
y = temp;
}
方法并没有改变存储在变量a和b的对象引用。swap方法的参数x和y被初始化为两个对象引用的拷贝,因此最终输入的两个参数并没有交换
/**
* This program demonstrates parameter passing in Java
*/
public class ParamTest {
public static void main(String[] agrs) {
/*
Test 1: Methods can't modify numeric parameters
*/
System.out.println("Testing tripleValue: ");
double percent = 10;
System.out.println("Before: percent = " + percent);
tripleValue(percent);
/*
Test 2: Methods can change the state of object parameters
*/
/*
Test 3: Methods can't attach new objects to object parameters
*/
}
public static void tripleValue(double x) { //doesn't work
x = 3 * x;
System.out.println("End of method: x = " + x);
}
public static void tripleSalary(Employee x){ //work
x.raiseSalary(200);
System.out.println("End of method: Salary = " + x.getSalary());
}
public static void swap(Employee x, Employee y){ //doesn't work
Employee temp = x;
x = y;
y = temp;
System.out.println("End of method: x = "+ x.getName());
System.out.println("End of method: y = "+ y.getName());
}
}
class Employee{
private String name;
private double salary;
//constructor
public Employee(String name, double salary){
this.name = name;
this.salary = salary;
}
public String getName(){
return name;
}
public double getSalary(){
return salary;
}
public void raiseSalary(double byPercent){
double raise = salary * byPercent / 100;
salary += raise;
}
}
4.1 类设计技巧
- 一定保证数据私有
- 一定要对数据初始化
- 不要再类中使用过多的基本类型
- 不是所有的域都需要独立的域访问器和域更改器
- 将职责过多的类进行分解
- 类名和方法名要能体现他们的职责
- 优先使用不可变类
第五章 - 继承
5.1.2 覆盖方法
public class Manager extends Employee{
…
public double getSalary(){
…
}
…
}
注意:
子类无法直接访问超类的私有域。也就是说尽管每个Manager对象都拥有一个名为salary的域,但在Manager类的getSalary方法中并不能直接访问salary域。只有借助Employee类的方法才能访问私有部分。
如果Manager 类的方法一定要访问私有域,就必须借助公有的接口,Employee类中的公有方法getSalary正是这样一个接口
但下面的这个做法仍然是错误的
public double getSalary(){
double baseSalary = getSalary(); //still won’t work
return baseSalary + bonus;
}
原因是Manager类也有一个getSalary方法,所以这个语句会无限制地调用自己。
因此,使用super来解决问题:
public double getSalary(){
double baseSalary = super.getSalary(); //work
return baseSalary + bonus;
}
子类可以增加域、增加方法或覆盖超类的方法,但绝对不能删除继承的任何域和方法。
5.1.3 子类构造器
public Manager(String name, double salary, int year, int month, int day){
super(name, salary, month, day);
bonus = 0;
}
super(name, salary, month, day); 是“ 调用超类 Employee 中含有 n、s、year month 和 day 参数的构造器” 的简写形式。
由于 Manager 类的构造器不能访问 Employee 类的私有域, 所以必须利用 Employee 类的构造器对这部分私有域进行初始化,我们可以通过 super 实现对超类构造器的调用。使用super 调用构造器的语句必须是子类构造器的第一条语句。
this 和 super 的用法:
成员变量:
this.变量 访问本类的成员变量
super.变量 访问父类的成员变量
构造方法:
this(…) 访问本类的构造方法
super(…) 访问父类的构造方法
成员方法:
this.方法() 访问本类的成员方法
super.方法() 访问父类的成员方法
5.1.5 多态
总结:
比如我们有一个父类Father,有一个子类Children
1、向上转型是自动的。即Father f = new Children()是自动的,不需要强转(儿子当爸爸)
2、向下转型要强转。即Children c = new Father()是无法编译通过的,必须要Children c = (Children)new Father(),让父类知道它要转成具体哪个子类(爸爸当儿子)
3、父类引用指向子类对象,子类重写了父类的方法,调用父类的方法,实际调用的是子类重写了的父类的该方法。即Father f = new Children(),f.toString()实际上调用的是Children中的toString()方法
向上转型**
public class UpAndDown {
public static void main(String[] args) {
// 向上转型:将父类引用指向子类对象
Father f = new Son();
f.sleep();//输出“爸爸睡觉”
//如果f.p();编译出错,不可执行。因为p()不是Father的方法。
}
}
向下转型
public class UpAndDown {
public static void main(String[] args) {
// 向下转型
Father f = new Son();
((Son)f).P();//输出“儿子调皮”
}
}
如果我们是直接向下转型呢?
public class UpAndDown {
public static void main(String[] args) {
// 直接向下转型
Father f = new Father();
f.sleep();//输出“爸爸睡觉”
((Son)f).P();//报错!!!!!!!!!!
}
}
直接向下转型竟然会报错!**
- 向上转型可以当做隐藏自身的方法,所以,再转回来(向下转型)方法就会恢复原状。该是你的还是你的。
- 直接向下转型,父类不具有子类的独有方法,所以即使转化成功,也是个残废儿子,还不如不转,所以直接报错转化失败。不是你的你想都别想。
5.19 抽象类
- 抽象类和抽象方法都要被abstract修饰,抽象方法一定要定义在抽象类中
- 抽象类不可以直接创建对象
- 只有覆盖了抽象类中所有的抽象方法后,其子类才可以创建对象。否则该子类还是一个抽象类 ```java package abstractClasses;
import java.time.LocalDate;
/**
This program demonstrates abstract classes. */ public class PersonTest { public static void main(String[] args){
Person[] people = new Person[2]; people[0] = new Employee("Warren Song",50000,1989,10,1); people[1] = new Student("Cathy","CS"); for(Person p : people){ System.out.println(p.getName() + ", " + p.getDescription()); }
} }
abstract class Person{ public abstract String getDescription(); private String name;
public Person(String name){
this.name = name;
}
public String getName(){
return name;
}
}
class Employee extends Person{ private double salary; private LocalDate hireday;
public Employee(String name, double salary, int year, int month, int day){
super(name);
this.salary = salary;
hireday = LocalDate.of(year,month,day);
}
public double getSalary(){
return salary;
}
public LocalDate getHireday(){
return hireday;
}
@Override
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;
}
}
class Student extends Person{ private String major; public Student(String name, String major){ super(name); this.major = major; }
public String getDescription(){
return "a student majoring in" + major;
}
}
---
<a name="36qRs"></a>
### 5.3 泛型数组列表
- **ArrayList<E>()**
用指定容量构造一个空数组列表。<br />参数: initalCapacity 数组列表的最初容量
- **boolean add(E obj)**
在数组列表的尾端添加一个元素。永远返回true。<br />参数: obj 添加的元素
- **int size()**
返回存储在数组列表中的当前元素数量。(这个值将小于或等于数组列表的容量。)
- **void ensureCapacity(int capacity)**
确保数组列表在不重新分配存储空间的情况下就能够保存给定数量的元素。<br />参数: capacity 需要的存储容量
- **void trimTosize()**
将数组列表的存储容量削减到当前尺寸<br />参数: index 位置(必须介于0 ~ size()-l 之间)
- **void set(int index,E obj)**
设置数组列表指定位置的元素值, 这个操作将覆盖这个位置的原有内容。<br />obj 新的值
- **E get(int index)**
获得指定位置的元素值。<br />参数: index 获得的元素位置(必须介于0 ~ size()-l 之间)
- **void add(int index,E obj)**
向后移动元素, 以便插入元素。<br />参数: index 插入位置(必须介于0 〜size()-l 之间)<br />obj 新兀素
- **E removed nt index)**
删除一个元素,并将后面的元素向前移动。被删除的元素由返回值返回。<br />参数:index 被删除的元素位置(必须介于0 〜size()-l 之间)
---
<a name="2kvxt"></a>
### 5.4 对象包装器与自动装箱(autowrapping)
简单来说,就是让基本数据类型的变量具有类中对象的特征。<br />基本数据类型,使用起来非常方便,但是没有对应的方法来操作这些基本类型的数据,可以使用一个类,把基本数据类型的数据装起来,这个类叫做包装类(wrapper)。这样我们可以调用类中的方法。<br />开发中,用的最多的是字符串变为基本数据类型。<br />**自动装箱**
```java
Integer i = 100;//自动装箱
//相当于编译器自动作以下的语法编译:Integer i = Integer.valueOf(100);
自动拆箱
Integer i = 10; //自动装箱
int t = i; //自动拆箱,实际上执行了 int t = i.intValue();
5.5 参数数量可变的方法
使用...
来实现
public static double max(double... values){
double largest = Double.NEGATIVE_INFINITY;
for(double v : values) if (v > largest) largest = v;
return largest;
}
编译器将 new double[ ] {3.1, 40.4, -5} 传递给max方法。
5.6 枚举类
基础用法
public enum SeasonEnum{
SPRING, SUMMER, FALL, WINTER;
}
进阶用法
// 创建一个枚举类
package com.lihaogn.test;
public enum SeasonEnum {
// 定义四个枚举实例
SPRING(1,"春"),SUMMER(2,"夏"),AUTUMN(3,"秋"),WINTER(4,"冬");
private final int id;
private final String name;
private SeasonEnum(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
}
// 测试类
package com.lihaogn.test;
public class Test {
public static void main(String[] args) {
for (SeasonEnum s : SeasonEnum.values()) {
System.out.println(s);
}
System.out.println(SeasonEnum.SPRING.getId());
System.out.println(SeasonEnum.SPRING.getName());
}
}
// result
SPRING
SUMMER
AUTUMN
WINTER
1
春
第六章 - 接口、lambda表达式与内部类
6.1.5 默认方法
default是在java8中引入的关键字,也可称为Virtual
extension methods——虚拟扩展方法。是指,在接口内部包含了一些默认的方法实现(也就是接口中可以包含方法体,这打破了Java之前版本对接口的语法限制),从而使得接口在进行扩展的时候,不会破坏与接口相关的实现类代码。
调用父接口实现
public interface Interface1{ default void helloworld(){ System.out.println("hi i'm from Interface1") } }
/** *实现接口Interface1 */ public class MyImplement implements interface1{ public static void main(String [] args){ MyImplement myImplement = new MyImplement(); //直接调用helloWorld()方法 myImplement.helloWorld(); } }
类优先于接口
public class MyImplement2 extends MyImplement implements Interface2{ public static void main(){ MyImplement2 myImplement2 = new MyImplement2(); MyImplement2.helloWorld(); } }
结果是运行MyImplement中的方法,类优先于接口
6.2.3 对象克隆