继承

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 }

 

    

 

   

posted @ 2020-11-27 23:45  Learn丶馒头  阅读(96)  评论(0)    收藏  举报