您的位置:首页 > 编程语言 > Java开发

java基础知识——继承

2014-03-18 10:08 441 查看
1.java中所有继承都是公有继承  ;

   子类扩展超类只需指出不同之处 ,如新添方法或覆盖超类方法; 当覆盖超类方法时,若想使用超类的域,需知:子类不能访问超类的私有域,超类的方法可作为访问接口,使用如super.getSalary()方法。super不是一个对象的引用,只是一个指示表一起调用超类方法的特有关键字。

   由于子类的构造器不能访问超类的私有域,必须利用超类的构造器对这部分私有域初始化,通过super实现对超类构造器的调用。

2.this两个用途:1引用隐式参数,2调用该类其它构造器  super两个用途:1调用超类方法 2调用超类构造器

3.现有超类employee,子类manager,下同。

一个对象变量(如e)可以引用多种实际类型的现象叫做多态,运行时能自动选择调用哪个方法的现象叫做动态绑定。如e.getSalary,当e引用employee对象时,调用的是employee中的方法,当e引用manager对象时,调用的是manager中的方法。

 java中不需要将方法声明为虚拟方法,动态绑定是默认的处理方式。

4.继承层次:java不支持多继承,具体实现参见接口。

  多态:is-a   ;  Manger boss = new Manager(..); Employee[] staff  = new Employee[3]; staff[0] = boss; 这里变量staff[0]与boss引用同一个对象,但编译器将staff[0]看做Employee对象,因此用boss调用Manager方法但是不能用staff[0]

       动态绑定:1.编译器查看对象的声明类型和方法名,假设调用x.f(param),且隐式参数x声明为c类的对象。一一列举所有c类中名为f的方法和其超类中访问属性为public且名为f的方法。

      2.编译器查看调用方法时提供的参数类型。在名为f的方法中存在一个完全匹配的参数类型,就选该方法。这个过程叫重载解析。允许类型转换。

      3.如果是private、static、final方法或构造器,则编译器可以准确知道应该调用哪个方法,称为静态绑定。

      4.程序运行中,采用动态绑定调用方法时,虚拟机一定调用与x所引用的对象的实际类型最符合的那个类的方法。是子类,还是超类(覆盖一个方法时,子类方法不能低于超类方法的可见性)

         虚拟机与显微每个类创建了一个方法表,列出了所有方法的签名和实际调用的方法。

    实例:调用e.Salary()的解析过程——

     1.虚拟机提取e的实际类型的方法表  2.虚拟机搜索定义getSalary签名的类,此时虚拟机已经知道应该调用哪个类  3.最后,虚拟机调用方法

5.阻止继承:

final类(final类中所有方法自动地成为final方法)不允许扩展,阻止人们定义该类的子类;类中方法定义为final时,子类不能覆盖这个方法。

6.强制类型转换:Manager  boss = (Manager) staff[0]; 进行类型转换的唯一原因是:在暂时忽视对象的实际类型之后,使用对象的全部功能。

   编译器允许将一个子类的引用赋值給一个超类变量,但是一个超类的引用赋值給一个子类变量,必须进行类型转换。

  注意2点:1.只能在继承层次内进行类型转换  2.在将超类转换为子类前,应使用instanceof进行检查。 3.一般,尽量少用类型转换和instanceOf运算符。

6.抽象类:抽象类中可以有多个抽象方法,建议尽量将通用的方法(无论抽象)在超类(无论抽象)中。类即使不含抽象方法也可以声明为抽象类。

abstract class Person{

 ...

public abstract String getDescription();

}

抽象方法充当占位角色,具体实现在子类中。扩展抽象类有2种方法:1.子类中定义部分抽象方法或抽象方法也不定义,这时子类也需标记为抽象类;2.定义全部的抽象方法,这时子类就不是抽象类了。

抽象类不能被实例化,一个类声明为abstract类,则不能创建该类对象。

! 但是可以定义一个抽象类的对象变量,但是只能引用非抽象类子类的对象  Person p = new Student("vanco","Politics");通过p.getDescription(),引用student子类对象中的方法。如果省略了person超类中的抽象方法,就不能通过变量p调用getDescription()方法了,编译器只允许调用在类中声明的方法。

7.受保护访问:

将方法或域声明为protected ,则允许子类访问。受保护的域要谨慎使用,因为其他人可以通过派生新类,访问受保护域。

受保护的方法更有实际意义,将方法声明为protected,表明子类得到信任,而其他类不行。

8.Object:所有类的超类

可使用object变量引用任何类型的对象。  java中只有基本类型不是对象(如数值、布尔类型、字符的值),所有的数组类型,不论是对象数组还是基本类型的数组都扩展与Object类。

下面针对几个常用方法,介绍一下:

  1.equals方法:检测一个对象是否等于另一个对象,该方法判断对象是否具有相同的引用,但更有意义的是,判断两个对象状态是否相等。下面是equals使用技巧:

 class Employee{

public boolean equals(Object otherObject)

       {

if(this == otherobject) return true;

if(OtherObject == null) return false;

  if(getClass != otherObject.getClass) return false;

Employee other = (Employee)otherObject;

return name.equals(other.name) &&salary == other.salary && hireDay.equals(other.hireDay);

}



总结上面,一个完美的equals方法有五步:

1.显式参数命名为otherObject,稍后要转换为另一个叫other 的变量。

2.检测this与otherObject是否引用同一个对象。实际上,这句只是一个优化。

3.检测otherObject是否为null,如果为null,返回false。

4.比较this与otherObject是否是与同一个类。若equals的语义在每个类中有所改变,就是用getClass检测;如果所有的子类都拥有统一的语义,就是用instanceOf检测:

if(!otherObject instanceOf className) return false;

5.将otherObject转化为相应的类类型变量

6.现在对所需要比较的域进行比较。

  equals具有下列特性:自反性;对称性;传递性;一致性;对于任意非空引用,x.equals(null)应返回false

 若在子类中重新定义equals,就要在其中包含调用super.equals(other)

对数组类型的域,可使用静态的Arrays.equals方法检测相应的数组元素是否相等。

  当隐式参数和显式参数不属于同一个类应怎么办:分为2种情况

1.若子类能拥有自己的相等概念,则对称性需求将枪支采用getClass进行检测

2.若超类决定相等的概念,那么就可以使用instanceOf进行检测,这样可以在不同的子类对象之间进行相等的比较。

  2.HashCode方法:

字符串的散列码是由内容导出的;而StringBuffer了的散列码是由Object类的默认hashcode方法导出的对象存储地址。

当需要重新定义equals方法,则必须重新定义hashCode方法,以便用户将对象插入到散列表中。

class{

public int hashcode(){

return 7*name.hashCode() + 11*new Double(Salary).hashCode()+13*hireDay.hashCode();

}

..

}

equals 与hashCode的定义必须一致,如果x.equals(y)返回true,那么x.hashCode必须与y.hashCode()具有相同的值。两个相等的对象要有相同的散列码。

若存在数组类型的域,可以使用静态的Arrays.hashCode方法计算一个散列码

   3.toString

   用于返回表示对象值的字符串,格式为:类名,随后是一对方括号括起来的域。强烈建议为每个自定义的类增加toString方法,是自己和所有使用该类的人受益。

最好通过调用getClass.getName获得类名的字符串,如果已经在超类中定义getClass.getName,子类只需要调用super.toString就好了。

只要对象与一个字符串通过操作符+连接起来,java编译器就会自动的调用toString方法,且打印一个对象x时,println也会直接调动x.toString方法。

不同的是,数组继承了object类中的toString方法,所以按旧方式打印,修正的方式调用静态方法Arrays.toString,多维数组调用Arrays.deepToString

9.泛型数组列表

         int actualSize= ...  Employee[] Staff = new Employee[actualSize] 允许在运行时确定数组列表,但一旦确定就不好修改大小。解决的最简单方法就是使用ArrayList,可自动调节数组容量,ArrayList<Employee> staff = new ArrayList<Employee>();
使用add方法添加元素 staff.add(new Employee("Harry Hacker",...));数组列表管理着对象引用的一个内部数组,如果调用add且内部数组已经满了,数组列表就将自动创建一个更大的数组,并将所有对象从较小的数组中拷贝到较大的数组中。
调用staff.ensureCapacity(100)将分配一个包含100个对象的内部数组,然后调用100次add,而不用重新分配空间;另外,还可以把初始化容量传递给ArrayList构造器:
ArrayList<Employee> staff = new ArrayList<Employee>(100),与new Employee[100]的区别是——第一个表示拥有保存100个元素的潜力,当重新分配空间的时候,将会超过100个元素,但最初初始化构造后不含任何元素。
staff.size返回数组列表中包含的实际元素数量,等价于数组a的length
一旦确定数组大小不再变化,可以调用trimToSize,将当前存储区域大小调整为当前元素数量所需的存储空间数目。
staff.set(i,harry);设置第i个元素。staff.get(i)获取数组列表的元素。
下面一个技巧可以一举两得,既可以灵活扩展数组,又可以方便访问数组元素:

首先创建一个数组,添加所有元素。

ArrayList<x> list = new ArrayList<x>();

while(...)

{

  x = ...;

  list.add(x);

}

然后使用toArray方法将数组元素拷贝到一个数组中

x[] a = new X[list.size()];

list.toArray(a);

除了在尾部追加元素,还可以在中间插入元素

int n= staff.size() / 2;

staff.add(n,e);

移除使用Employee e = staff.remove(n);

如果数组存储的元素比较多,又经常需要在中间位置插入删除元素,就应该考虑使用链表了。

10.对象包装器与自动打包:
将int这样的基本类型转换为对象。所有的基本类型都有一个对应的对象。
对象包装器类有:Integer、Long、Float、Double、Short、Byte、Character、Void、Boolean;对象包装器是不可变的,即一旦构造了对象包装器,便不允许更改包装器在其中的值。同时,它们还是final类,因此不能定义他们的子类。
假设想定义一个整型数组列表,而尖括号中参数不允许是基本类型,这时我们可以用:

ArrayList <Integer> list = new ArrayList<Integer>();      但是ArrayList <Integer>效率远低于int[]数组,因此更应使用它来构造小型集合。
自动打包:list.add(3)会被自动变换为list.add(new Integer(3));
自动拆包:当一个integer对象赋值给一个int值时,会自动拆包,int n = list.get(i)被翻译为 int n = list.get(i).intValue() ;

在算术表达式中也能自动打包拆包。
==运算符也可应用于对象包装器,检测的是对象是否指向同一个区域。
最后,通常我们可以将某些基本方法放置在对象包装器中,如将一个字符串转化为整型 int x = Integer.parseInt(s);这里与Integer没关系,parseInt是一个静态方法,但Integer类是放这个方法的好地方

11.参数数量可变的方法

public Pritnstream printf(String fmt, Object... args){}   ...表明这个方法可以接受任意数量的对象。

12.枚举类

声明 public enum size{SMALL,MEDIUM,LAREGE,EXTRA_LARGE} 

enum Size

{
SMALL("S"),MEDIUM("M"),LARGE("L"),EXTRA_LARGE("XL");
private Size(){   }

}

所有枚举类型都是Enum的子类。

Size.SMALL.toString返回字符串“SMALL”  逆的静态方法valueOf ,Size s  = (size)Enum.valueOf(Size.class,"SMALL")将s设置为Size.SMALL

Size[] values = Size.values[] 返回一个包含全部枚举值的数组。

13.继承设计的技巧:
1.将公共 操作和域放在超类
2.不要使用受保护的域
3.使用继承实现is-a
4.除非所有继承的方法都有意义,否则不要使用继承。
5.在覆盖原方法时,不要改变预期的行为。
6.使用多态,而非类型信息。
7.不要过多使用反射。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java基础