继承
1.方法重写
签名:方法的名字和参数列表
重写的要求:在在父类关系中,签名相同,子类的访问权限>=父类的访问权限,子类的返回值类型(层次)<=父类的返回值类型
2.this和super
子类可以继承父类的所有属性和方法,包括私有属性和方法,但是子类不能直接访问父类的私有域,只能通过父类的公有接口进行访问。
this:是对调用者对象本身的引用,可以访问方法,也可以访问域。
super:不是对父类对象的引用,只能访问父类的方法,而不能访问域,也不能将super赋给另一个对象变量,仅仅是一个调用父类方法的特殊关键字。
但是两者用法类似:
this:一是调用隐式参数,二是调用该类的其他构造器;
super:一是调用父类方法,二是调用父类的构造器;
3.子类构造器
子类对父类的私有域的初始化必须通过父类的构造器。
super调用构造器的语句必须是子类构造器的第一条语句。
子类构造没有显式的调用父类构造器,则默认调用父类的无参构造器,如果父类没有给出无参构造器,子类也没有显式的调用父类的其他构造器,则编译报错。
4.继承层次
Java不支持多继承,但是可以多层继承。
5.多态
多态:一个对象变量可以指示多种实际类型(父类引用指向子类对象)。
动态绑定:在运行时,自动选择调用哪个方法(针对的是方法而不包括域)。
可以将子类以及子类数组的引用赋值给父类变量,但是反之不行。
被private,static,final修饰的方法,因为不能重写所以编译器知道该调用哪个方法,这叫做静态绑定;
6.final阻止继承
把类用final修饰,则不能定义子类,final修饰方法,则子类不能重写该方法。final修饰类,其中所有的方法都将被final修饰,但是其中的域不能自动被fianl修饰。
7.强制类型转换
条件:
1,只能在继承层次内进行类型转化;
2.将超类转换为子类之前一定使用instanceOf继续判断;instanceOf判断是否属于同一类型,左右两边必须有子父类或实现关系,其中左边的层级一定要低于右边的层级才会返回true,否则返回false。
8.抽象类
抽象类中可以有非抽象的方法,只要是通用的域和方法,都建议放在超类中(不管是否抽象),类中即使不包含抽象方法也可以声明为抽象类(为了不被实例化);
子类必须重写所有的抽象方法,否则子类也必须定义为抽象类。
9.受保护访问
protected:一般用来修饰方法很少用来修饰域。
四种权限修饰符:
public --------- 对所有类可见;
protected ----------- 对本包和所有子类可见;
默认 ------------- 对本包可见;
private ------------- 仅对本类可见;
10.Object类
在Java中只有基本类型不是对象。Java中所有的类都由Object类进行扩展。
equals方法:
在Object类中equals用来比较两个对象是否有相同的引用。
equals方法和==的区别:
==比较基本数据类型比较的是值,比较引用数据类型比较的是地址。
equals不能比较基本数据类型,equals比较引用数据类型如果没有重写则比较地址,如果重写则比较内容。
注意:调用equals方法时为防止空指针异常,可以使用Objects,equals(a,b)代替 a.equals(b);
重写equals方法:
相等检测可以使用getClass()或者instanceOf但是两者各有利弊。
重写euqals的示范代码:
EqualsTest:
1 package com.base.equals; 2 3 import java.time.LocalDate; 4 5 public class EqualsTest { 6 public static void main(String[] args) { 7 //定义两个状态相同的Emloyee对象 8 LocalDate date = LocalDate.now(); 9 Employee e1 = new Employee("zs",10, date); 10 Employee e2 = new Employee("zs",10, date); 11 System.out.println("e1.equals(e2) is " + e1.equals(e2)); //true 如果改用intanceOf判定相等 true 12 //定义两个状态相同的Manager对象 13 Manager m1 = new Manager("zs",10, date,20); 14 Manager m2 = new Manager("zs",10, date,20); 15 System.out.println("m1.equals(m2) is " + m1.equals(m2)); //true 如果改用intanceOf判定相等 true 16 //一个Emloyee对象和一个Manager对象比较 17 System.out.println("e1.equals(m1) is " + e1.equals(m1)); //false 如果改用intanceOf判定相等 true 18 System.out.println("m1.equals(e1) is " + m1.equals(e1)); //false 如果改用intanceOf判定相等 报强制类型转换的错 如果父类equals方法使用final修饰子类不能重写 返回true 19 } 20 }
Employee:
1 package com.base.equals; 2 3 import java.time.LocalDate; 4 5 public class Employee { 6 private String name; 7 private double salary; 8 private LocalDate hireDate; 9 10 public Employee() { 11 } 12 13 public Employee(String name, double salary, LocalDate hireDate) { 14 this.name = name; 15 this.salary = salary; 16 this.hireDate = hireDate; 17 } 18 19 public String getName() { 20 return name; 21 } 22 23 public void setName(String name) { 24 this.name = name; 25 } 26 27 public double getSalary() { 28 return salary; 29 } 30 31 public void setSalary(double salary) { 32 this.salary = salary; 33 } 34 35 public LocalDate getHireDate() { 36 return hireDate; 37 } 38 39 public void setHireDate(LocalDate hireDate) { 40 this.hireDate = hireDate; 41 } 42 43 public final boolean equals(Object otherObject){ 44 if(this == otherObject) 45 return true; 46 if (otherObject == null) 47 return false; 48 // if (getClass() != otherObject.getClass()) return false; 49 if (!(otherObject instanceof Employee)) return false; 50 Employee employee = (Employee) otherObject; 51 return name.equals(employee.name) && salary == employee.salary && hireDate == employee.hireDate; 52 } 53 }
Manager:
1 package com.base.equals; 2 3 import java.time.LocalDate; 4 5 public class Manager extends Employee { 6 private double bonus; 7 8 public Manager() { 9 } 10 11 public Manager(String name, double salary, LocalDate hireDate, double bonus) { 12 super(name, salary, hireDate); 13 this.bonus = bonus; 14 } 15 16 17 18 public double getBonus() { 19 return bonus; 20 } 21 22 public void setBonus(double bonus) { 23 this.bonus = bonus; 24 } 25 26 // public boolean equals(Object otherObject){ 27 // if (!super.equals(otherObject)) return false; 28 // Manager other = (Manager) otherObject; 29 // return bonus == other.bonus; 30 // } 31 }
注意:通过子类的方法访问父类,父类的this代表子类对象。
重写equals方法的建议:
1.显示的参数名命名为otherObject,因为之后需要强制转换为other变量,便于区分。
2.检测this与otherObject是否引用同一个对象。
if(this == otherObject) return true;
3.检测otherObject是否为null。
if(otherObject == null) return false;
4.比较this与otherObject是否属于同一个类,如果equals的语义在每个子类中有所改变,就使用getClass检测:
if(getClass() != otherObject.getClass()) return false;
如果所有的子类都拥有统一的语义,就使用instanceOf进行检测并且把父类的euqals用final修饰(因为instanceOf会把父类或者它实现的接口也认定为同一个类型,如果子类相等的语义不统一会违反Java对equals的规范,对称性和传递性得不到保证);
if(!(otherObject intanceOf ClassName)) return false;
5.将otherObject转换为相应类类型的变量;
ClassName other = (ClassName) otherObject;
6.对所有需要比较的域进行比较,基本类型使用==,引用类型使用equals比较。
return field == other.field1 && Objects.equals(field2,other.field2) && ....;
7.如果子类重新定义equals,就要在其中包含super.equals(otherObject);
注意:方法重写时一定显式参数类型一定要是Object,否则并不能覆盖原始的euqals方法,因为签名不同,不能算是方法的重写,所以在开发时可以使用@Override进行检查。
hashCode方法:
散列码是由对象导出的一个整型值。
两个不同的对象可能hashCode相同但是几率很小,hashCode不同的对象肯定不是同一个对象。
Object类中的hashCode方法返回的是对象存储地址的映射(存储地址经过哈希算法),所以每个对象都会有一个默认的散列码。
String类重写了hashCode,比较的是内容,所以相同内容的字符串返回相同的散列码。
如果重新定义equals方法,就必须重写hashCode方法。因为首先违反Java规范,如果不同时重写使用equals方法和hashCode方法比较两个对象返回的结果不一致,再有HashSet/HashMap/HashTable都是先根据hashCode值进行存取的,由于没有重写hashCode,不同对象返回的hashCode一般不同,所以使用hashMap可能会造成key重复,使用取值时取不出对应内容的对象。
如果存在数组类型的域,可以是使用Arrays.hashCode方法计算一个散列码。
hashCode重写一般根据equals判定相等的域进行重写:
1 @Override 2 public boolean equals(Object otherObject){ 3 if(this == otherObject) 4 return true; 5 if (otherObject == null) 6 return false; 7 // if (getClass() != otherObject.getClass()) return false; 8 if (!(otherObject instanceof Employee)) return false; 9 Employee employee = (Employee) otherObject; 10 return name.equals(employee.name) && salary == employee.salary && hireDate == employee.hireDate; 11 } 12 13 @Override 14 public int hashCode() { 15 return Objects.hash(name, salary, hireDate); 16 }
toString方法:
Object类定义的toString方法,用来打印输出对象所属的类名和散列码。例如:
System.out.println(System.out); 输出结果:java.io.PrintStream@2f6684。因为PrintStream类没有重写toString。
数组继承了Object的toString并没有重写,修正的方法是Arrays.toString(arr)。如果想打印多维数组的内容则使用Arrays.deepToString(arr);
对象与一个字符串通过“+”连接,会自动调用该对象的toString方法,用“+x”代替x.toString()的好处是:如果x为基本类型,程序也不会报错。
类设计toString 的一般格式:
1 @Override 2 public String toString() { 3 return "Employee{" + 4 "name='" + name + '\'' + 5 ", salary=" + salary + 6 ", hireDate=" + hireDate + 7 '}'; 8 }
优化后:
1 @Override 2 public String toString() { 3 return getClass().getName() +"{" + 4 "name='" + name + '\'' + 5 ", salary=" + salary + 6 ", hireDate=" + hireDate + 7 '}'; 8 }
这样写的好处是子类只需要调用super.toString() + 自己特有的域就可以了。
11.泛型数组列表
ArraysList<Element> staff = new ArraysList<>();
如果能够估计出数组可能的元素数量,就可以在填充之前调用ensureCapacity()方法,也可以直接传递给构造器ArraysList<Element> staff = new ArraysList<>(100);
数组列表的容量与数组的大小非常重要的区别就是,如果为数组分配100个空间,则数组就有100个空位可以使用,而容量为100个元素的数组列表只是拥有包含100个元素的潜力,在最初初始化元素列表的时候,一个元素也不包含。
使用size()方法返回数组列表中实际的元素数目:
staff.size();
明确数组列表不会再有新的元素添加的时候可以调用trimToSize(),将存储区域的大小调整为当前元素数量所需的存储空间数目。
访问数组列表:
使用add方法为数组添加新元素, 而不要使用set方法,set只能替换数组中已经存在的元素内容。
在数组列表中间添加数据可以使用add(i,e),位于i之后的元素都会向后移动;
移除一个元素使用remove(i,e);位于i之后的元素都要向前移动一个位置;
12.对象包装器与自动装/拆箱
可以定义一个数组列表,由于尖括号中类型参数不能使用基本类型,所以我们可以声明一个这样的数组列表:
ArrayList<Integet> arr = new ArrayList<>();
注意:因为每个值分别包装在对象中,所以数组列表的效率远低于数组,所以应该用它来构造小集合。
自动装箱规范要求:
Boolean,Byte,Char <= 127,-127 <= short,int <= 127.
注意:两个包装器对象比较时,使用equals
装箱和拆箱是编译器认可的,不是虚拟机。
13.参数数量可变的方法
计算若干个数的最大值:
1 package com.base.Test; 2 3 public class MaxPlus { 4 public static void main(String[] args) { 5 double max = max(3.0,22,14,2,25,77.1); 6 System.out.println("最大值为:" + max); 7 } 8 9 public static double max(double...values){ 10 double largest = Double.NEGATIVE_INFINITY; //Double.NEGATIVE_INFINITY 表示负无穷大 11 for (double value : values) { 12 if(value > largest){ 13 largest = value; 14 } 15 } 16 return largest; 17 } 18 }

浙公网安备 33010602011771号