Java基础之继承

1,利用继承,人们可以基于已存在的类构造一个新类。继承已存在的类就是复用(继承)这些类的方法和域。在此基础上添加一些新的方法和域,以满足新的需求(is-a)。

2,反射是指程序运行期间发现更多的类及其属性的能力。

3,overload重载,override覆盖

4,super关键字与this关键字区别:

    1)this关键字相当于对象的引用;

    2)super不是引用,不能将super赋给一个对象变量,它只是一个指示编译器调用超类方法的特殊关键字;

    3)this一是引用隐式参数,二是调用该类的其它构造器;

    4)super一是调用超类方法,二是调用超类的构造器。

5,子类中可以增加数据域、方法,或覆盖父类的方法,但是不能删了继续的数据域或方法;

6,子类构造器可以使用super()显示调用父类的构造器,如果子类不显示调用,则子类会默认调用父类的无参构造器;

如果子类没有显示的调用父类的构造,但父类又没有默认、无参构造,则编译器会报错。

7,一个对象变量(如e)可以指示多种实际类型的现象被称为多态,在运行时能够自己动地选择调用哪个方法的现象称为动态绑定。

public class Test {
    public static void main(String[] args) {
        Manager boss = new Manager("test", 8000, new Date());
        boss.setBonus(5000);

        Employee[] staff = new Employee[3];
        staff[0] = boss;
        staff[1] = new Employee("test1", 7000, new Date());
        staff[2] = new Employee("test2", 6000, new Date());

        for (Employee e : staff) {
            System.out.println(e.getName() + "  " + e.getSalary());
        }
    }
}
public class Employee {

    private String name;

    private double salary;

    private Date hireDay;

    public Employee() {

    }

    public Employee(String name, double salary, Date hireDay) {
        this.name = name;
        this.salary = salary;
        this.hireDay = hireDay;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public Date getHireDay() {
        return hireDay;
    }

    public void setHireDay(Date hireDay) {
        this.hireDay = hireDay;
    }
}
public class Manager extends Employee {

    private double bonus;

    public Manager() {

    }

    public Manager(String name, double salary, Date hireDay) {
        //初始化父类的构造,必须放在第一句
        super(name, salary, hireDay);
        this.bonus = 0;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }

    public double getSalary() {
        //super.getSalary() 调用父类的方法,只有Employee中的方法才能够访问其数据域
        return bonus + super.getSalary();
    }
}

8,对象变量是多态的,每个对象变量都属于一个类型;

9,动态绑定:

    1)x.f(param),x为C类的对象,首先编译器会获得所有C类对象(即C类的父类、子类)所有public修饰的f()方法;

    2)编译器查看调用方法的参数类型,如String类型参数,hello('test'),如果在父类或子类中存在多个类型相同的方法,编译器会报错,至此编译器与获得所有方法名和参数类型;

    3)如果是static,private,final方法,编译器准确的知道调用哪个方法,这叫“静态绑定”;与之相对的是动态绑定;

    4)程序运行时JVM一定调用与x所引用对象类型最合适的那类的方法,假设x的实际类型是D,D是C的子类,所以JVM会调用D的f()方法;

    5)JVM每次运行时去会进行搜索,时间开销相当大,所以会建以一个方法表,需要时从方法表中读取。

10,子类覆盖父类方法时,访问权限不能低于父类的访问权限,如父类是public,子类也必须是public;反之编辑器会把它解释为试图降低访问权限;

11,阻止继承,final类、方法;

    1)final类不允许被继承;如果类被声明为final,其类中的方法自动的转为final方法(不允许类继承,所以方法也是不能继承的),例如:String是final类,不允许继续;其作用主要是确保子类不会改变其语义。

    2)final方法,子类不允许覆盖;

    3)fina域,构造初始化对象过后不再允许更改它的值。

12,早期java中使用final关键字来避免动态绑定,如果一个方法较短并且没有被覆盖,编译器就会对其进行内联优化,例如:e.getNmae()将被优化成e.name;目前JVM的即时编译器比传统编译器处理能力强得多,可以对那些较短的方法,调用频率高的类进行自动优化,如果后期继承的子类对优化的方法有覆盖,优化器将取消内联优化,这个过程很慢,但很少发生。

13,类型转换,使用is-a原则,子类可以强制转换为基类,反之不行;ClassCastException

if(staff[1] instanceof Manager){
    boss = (Manager)staff[1];
}

14,包含一个或多个抽象方法的类必须被声明为抽象类;抽象类同时包括具体数据和具体的方法;

abstract class Person{
    private String name;
    
    public abstract String getDescription();
    
    public Person(String name){
        this.name = name;
    }
    
    public String getName(){
        return name;
    }
}

15,类不包含抽象方法也可以声明为抽象,抽象类不可以被实例化。但可以定义抽象类的对象变量:

class Student extends Person{
}

//Pserson为抽象类,student为实现的子类
Person p = new Student("test", "test");
public abstract class Person {
    private String name;

    public abstract String getDescription();

    public Person(String name) {
        System.out.println("Person构造");
        this.name = name;
    }

    public String getName() {
        return name;
    }

}
public class Student extends Person {

    private String major;
    
    //父类中存在有参构造,子类一定要先调用父类构造,如果是默认的,可以不调用super
    public Student(String name, String major) {
        super(name);
        System.out.println("Student构造");
        this.major = major;
    }

    @Override
    public String getDescription() {
        return "a student major in " + major;
    }

}
public class Employee extends Person {

    private double salary;
    private Date   hireDay;

    public Employee(String name, double salary, Date hireDay) {
        super(name);
        System.out.println("Employee构造");
        this.salary = salary;
        this.hireDay = hireDay;
    }

    @Override
    public String getDescription() {
        return String.format("an example with a salary of $%.2f", salary);
    }

    public double getSalary() {
        return salary;
    }

    public void raiseSalary(double byPercent) {
        double raise = salary * byPercent / 100;
        salary += raise;
    }
}
public static void main(String[] args) {
        Person[] persons = new Person[2];
        persons[0] = new Student("student", "class");
        persons[1] = new Employee("emplyee", 9000, new Date());
        for (Person person : persons) {
            System.out.println(person.getName() + ", " + person.getDescription());
        }
    }
输出结果:
Person构造
Student构造
Person构造
Employee构造
student, a student major in class
emplyee, an example with a salary of $9000.00

16,子类同样无法访问父类的private域;

17,protected方法比protected域更有实际意义,可以让子类调用父类的方法,其它包外的类不允许;

18,protected与defalut的区别:

    1)protected,子类、包内访问权限,但这里有一点需要注意:如果子类在包外,也是可以访问的;

    2)default,包内访问权限,没有子类这种定义,相当于只能在包内才能访问,只要在包外都不可以访问;

19,访问权限从大到小的顺序:

    public->protected->default->private

20,Object类是java所有类的超类,java中的每个类都是由object类扩展而来,如果没有明确指出超类,Object就是这个类的超类;

 //object只能持有引用,不能对内容进行操作
Object obj = new Employee("", "");

21,在java中只有基本类型不是对对象,例如:数值,字符,布尔型的值都不是对象,8种基本数据类型;

22,Object的equals比较的是两个对象引用是否相等

23,==比较基本数据类型, equals比较对象域(数据),如果都相等,则返回true;

24,hashcode,散列码,是由对象导出的一个整型值,两个对象的.hashCode()基本不会相同;

25,由于hashCode方法定义在Object类中,因此每个对象都有一个默认的hashcode,其值为对象的存储地址

26,

        String s = "OK";
        StringBuilder sb = new StringBuilder(s);
        System.out.println(s.hashCode() + " " + sb.hashCode());
        String t = new String("OK");
        StringBuilder tb = new StringBuilder(t);
        System.out.println(t.hashCode() + " " + tb.hashCode());
        
        结果:
        2524 14576877
        2524 12677476

    为什么s、t的散列码相同?因为String重写了Object的hashcode,导出内容的散列码;

    然而sb、tb的散列码不同?因为StringBuilder没有重写Object的hashcode,采用了Object的默认方法,导出的是对象的存储地址。

27,equals与hashCode的定义必须一致:如果x.equals(y)返回true,那个x.hashCode()必须与y.hashCode()具有相同的值(存储地址);

    Arrays.hashCode();     

    hashCode(null) = 0,hashcode可以为正数或负数;

28,toString(对对象来说x.toString()与""+x是等价的,但如果x是基本类型则会直接执行)

    @Override
    public String toString() {
        return super.toString() + "Employee[name=" + super.getName() + ", salary=" + salary
               + ", hireDay=" + hireDay + "]";
    }

29,toString打印数组

        int[] array = {1,2,3,4,5,6};
        System.out.println(Arrays.toString(array));
        //打印多维数组
        Arrays.deepToString();

30,ArrayList动态数组,在数组已满的情况下,会重新创建一个更大的数组,然后将较小的数组copy到较大的数组中;

    1)数组列表管理着对象引用的一个内部数组;

    2)JAVA7中,如果确定一个数组列表容量,可以提前分配数组的列表容量arraylist.ensureCapacity(100);同时也可以采用以下方式:

List<String> arrayList = new ArrayList<String>(100);

    3)数组列表容量与数组大小有区别(new Employee[100]),分100个空间,说明数组有100个空位置可用;100个列表容量表示只是拥有保存100个元素的潜力,实际上,重新分配空间的话会超过100;

31,ArrayList与数组区别:

    1)数组,固定长度,效率较ArrayList高;

    2)ArrayList,动态数组,可变长度,无初始化数组长度,底层当然是用数组来实现;

    3)ArrayList由于增加了灵活性,所以较数组效率低;

        List<String> arrayList = new ArrayList<String>(100);
        for (int i = 0; i < 20; i++) {
            arrayList.add(String.valueOf(i));
        }
        //数组
        String[] arrays = new String[arrayList.size()];
        //转换为数组
        arrayList.toArray(arrays);
        System.out.println(Arrays.toString(arrays));
        输出结果:
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

32,每个基本数据类型都有一个包装器类,如:Integer,Long,Float,Double,Short,Byte,Void,Boolean,其中前6个派生于公共的超类Number。

    1)Integer.valueOf(int),自动装箱

    2)list.get(i).intValue(),自动拆箱

    3)装箱和拆箱是编译器认可的,不是JVM

33,继承设计:

    1)将公共操作、域放在超类;

    2)不要使用受保护的域;protected,破坏封装,无限子类可以访问;同一个包中可以访问导致不是子类也可以访问;但需要在子类中重新定义的可以使用;

    3)使用继承实现“is-a”关系,继承很容易达到节省代码的目的;

    4)除非所有的继承方法都有意义,否则不要继承;

    5)在覆盖方法时,不要改变预期的行为;置换原则不仅应用于语法,而且也可以应用于行为;

    6)使用多态,而非类型信息(根据类型来判断使用哪个方法);

    7)不要过多的使用反射。反射是很脆弱的,编译器很难发现程序中的错误,只有在运行时发现,并导致错误或异常。


你可能感兴趣的:(Java基础之继承)